Bots
Telegram channel autoposting bot: how to schedule posts
In short
A Telegram channel autoposting bot is a program added to the channel admins with permission to post, which sends posts at the right time on its own. The Bot API has no scheduled sending, so the schedule lives in the bot code: in a library such as APScheduler or node-cron, or in a simple loop that checks a post queue once a minute. Keep the queue in a file or an SQLite database, and always set the time with an explicit time zone, because servers usually run on UTC. For posts to go out without gaps the bot has to run around the clock, for example on a host like Netrun on the Pro plan.
- The Telegram Bot API has no scheduled sending, so a bot publishes a post at the moment sendMessage is called and the schedule lives in code.
- A bot can only post to a channel after it is added as a channel admin with the permission to post messages.
- In bot code a public channel is referred to as @channelname, while a private channel needs a numeric ID that starts with -100.
- Servers usually run on UTC, so a post scheduled for 9:00 without a time zone goes out at 12:00 Moscow time.
- A post queue kept only in program memory disappears every time the bot restarts.
Running a channel and publishing posts by hand every day gets tiring, so sooner or later you want a bot to post on a schedule for you. The idea is right, but there is a catch almost everyone runs into. Telegram bots have no send later button like the app does: a bot sends a message exactly when its code calls the send method. So the schedule has to live inside the bot, and the bot has to run all the time.
Below is how to connect a bot to a channel, where to keep the post queue, which scheduling method to choose and why a post scheduled for nine in the morning sometimes goes out at noon. If the bot is already written and the only question is where to keep it, start with the guide on running a bot 24/7.
| Method | When it fits | What to watch out for |
|---|---|---|
| Scheduled messages in the Telegram app | A few posts a week that you write by hand anyway | This is an app feature for people, bots cannot use it |
| A loop with a pause in code | One post a day at the same time | The countdown restarts after every restart, so the time easily drifts |
| APScheduler (Python) | Several schedules, cron-style expressions, a bot on aiogram or another framework | Set timezone explicitly; by default jobs live in memory |
| JobQueue in python-telegram-bot | The bot is already written with python-telegram-bot | Installed as the job-queue extra of the library |
| node-cron (Node.js) | A bot on Telegraf, grammY or another Node.js library | The schedule has a timezone option, use it |
| A queue in SQLite checked once a minute | A content plan weeks ahead that you edit without republishing code | Keep the database in a persistent folder and mark sent posts |
Create the bot and make it a channel admin#
Create a bot in BotFather and save its token. Then open the channel settings, add the bot as an admin and leave it the permission to post messages; the other permissions can be turned off. Without that permission Telegram replies with an error saying the bot needs admin rights in the channel, and the post does not go out.
Point the bot at the channel#
A public channel is referred to by its short name like @channelname. A private channel has no name, so you need its numeric ID, which starts with -100: one way to find it is to forward a post from the channel to one of the bots that display chat IDs. Keep the channel ID and the token in environment variables rather than in code, so the bot is easy to switch to a test channel.
Put your posts into a queue#
A queue is a list of posts with text, an image, the publish time and a flag saying whether the post was sent. A JSON file is enough to start, and for a content plan weeks ahead an SQLite database is more convenient. The key is to keep the queue on disk rather than in program memory, and to set the sent flag right after a successful publish, so the bot does not repeat a post after a restart.
Choose a scheduling method#
The simplest option is a loop that checks the queue once a minute and publishes everything that is due. If you have several schedules, a library is more convenient: APScheduler for Python, JobQueue in python-telegram-bot, or node-cron for Node.js. Decide in advance what to do with posts whose time passed while the bot was off: publish them on startup or skip them. In APScheduler this is controlled by the misfire_grace_time setting.
Set the time zone explicitly#
Servers usually run on UTC, and a time without a zone is read exactly that way: nine in the morning UTC is noon in Moscow. Set the zone right in your code: the timezone parameter in APScheduler and node-cron, or ZoneInfo with a zone such as Europe/Moscow in Python. If Python says it does not know the zone, add the tzdata package to requirements.txt.
Publish the bot and test it on a test channel#
Create a private test channel, add the bot there and schedule a couple of posts a few minutes ahead to see the result right away. Netrun has no separate task scheduler like cron, and you do not need one: the schedule runs inside the bot, and the bot runs as a long-lived program. Code is uploaded as an archive, a folder or from GitHub, the token goes into Secrets, and the SQLite queue goes into the persistent /data folder, which survives code updates.
Telegram channel autoposting comes down to three parts: a bot with permission to post, a post queue on disk and a schedule with an explicit time zone. The weakest link is usually not the code but the place where the bot runs: if the laptop goes to sleep, the 7 a.m. post will not go out. On Netrun the bot is published from code with no server setup, the queue in /data survives updates, and the logs show every post. On the free plan a bot runs for a limited time, which is enough to test the schedule, while daily posts without gaps need Pro. Try Netrun.
Common questions
Why did my post go out three hours later than scheduled?
Almost certainly the schedule runs on UTC while you entered Moscow time. The server reads nine in the morning as nine UTC, which is noon in Moscow. Set the time zone right in the scheduling code and posts will go out on time.
Why can my bot not post to the channel?
Most often the bot was not added as a channel admin, or its permission to post messages is turned off. The second common cause is a wrong channel ID: a private channel needs a numeric ID starting with -100, not an invite link. The exact reason appears in the bot logs in the text of the Telegram response.
Can I schedule Telegram posts without a bot running all the time?
By hand, yes: the Telegram app has scheduled messages, and for a couple of posts a week that is enough. A bot cannot do that, because the Bot API has no scheduled sending, so automatic posting needs a program that is running at the moment of publishing. If the program is off, the post does not go out.
Where did my post queue go after I updated the bot?
Most likely it was stored in program memory or in a file next to the code, and such data is recreated on every publish. Keep the queue in a persistent folder: on Netrun that is /data, which survives code updates and restarts. Store the records of sent posts there as well.
Can one bot manage several channels?
Yes. Add the bot as an admin to each channel and store in the queue which channel every post belongs to. Different channels can have different schedules, and you can set a time zone per channel if the audiences live in different countries.
Is there a way to run something on a schedule?
There is no separate schedule setting in the dashboard. Scheduling is done inside the application itself: the script runs continuously and decides when to act — with a loop and a pause, or with a scheduling library. A project like that counts as a background script: it does not sleep, so on the free plan it runs for 3 hours, and continuous operation needs the Pro plan.
Is my data kept when I update the code?
Yes, as long as your data lives in persistent storage. For a regular project that is the /data folder: anything your app saves there stays in place when you update the code or restart, and you can browse and download the files on the "Data" tab. If you described the services yourself in docker-compose, persistent storage means the named volumes from your file, and such a project has no /data folder. Files written outside persistent storage are created from scratch on the next deploy. Your project link stays the same either way.
What happens to my bot after 3 hours on the free plan?
The bot switches off, but your code, settings and secrets stay where they are — we delete nothing. Shortly before it switches off we send you a warning. The three hours only count while the bot is actually running: while it is stopped, still building or crashed with an error, the clock stands still — you can fix the code and start it again without losing time. Switch to the Pro plan, and the bot starts again from the same place and runs around the clock. A new bot uploaded after that is built too, but it will not start: free time is counted per account, not per project.