Telegram Bot API via Proxy: Setting Up Long Polling, Webhooks, and Handling 429 Errors
Table of contents
- Introduction: what you'll get from this guide
- Preparation: tools and access
- Core concepts: how the telegram bot api works
- Step 1: create a bot and get the token
- Step 2: connect the proxy and verify api access
- Step 3: run long polling through the proxy
- Step 4: set up a webhook with https and a secret token
- Step 5: handle 429 errors and make requests robust
- Step 6: advanced setup for high-load projects
- Verifying the result: a checklist for a finished solution
- Common mistakes and their solutions
- Faq: common setup questions
- Conclusion
Introduction: What You'll Get from This Guide
If you've ever launched a bot on Telegram, you know one annoying truth. The bot itself takes an evening to write, but making it work reliably turns into a whole separate project. Messages arrive late, updates get lost, the server suddenly gets a 429 error, and the webhook just stops being called without any explanation. Add a proxy that all requests must go through, and the number of failure points doubles.
This step-by-step guide solves exactly these problems. You'll learn to work with the Telegram Bot API through a proxy so your bot doesn't crash, doesn't lose messages, and responds correctly to rate limits. We won't cover the MTProto protocol or build a proxy monitoring system. Our topic is narrower and more practical: HTTP requests to api.telegram.org through a proxy, two ways to receive updates, and the right way to handle limits.
What You'll Have at the End
- A working bot that sends requests to the Telegram Bot API through your proxy (HTTP or SOCKS5).
- Long polling set up so it doesn't break on timeouts or duplicate messages.
- A webhook with HTTPS and a secret token that receives updates on your server.
- A ready-made request wrapper that automatically waits the right amount of time on a 429 error and retries.
- An understanding of which mode to choose for your project: polling or webhook.
Who This Guide Is For
Marketers and affiliates building bots for broadcasts, lead notifications, and campaign stats. Developers who need a fixed, predictable outbound IP for server requests. Business owners whose bots serve customers and can't afford to go silent for an hour. If you work with mobile proxies and want to route bot traffic through them, you're in the right place.
What You Should Know Beforehand
We explain every step from scratch, but a few basic skills will make life much easier. It helps to know how to open a terminal or command prompt, copy commands, and run Python scripts. Knowing what an HTTP request and JSON are is a plus, but we'll cover them in plain language. Advanced topics are in a separate section that beginners can skip without losing the end result.
How Much Time It Takes
Preparing and creating the bot takes about 20 minutes. Setting up the proxy and making the first successful request takes another 20-30 minutes. You'll get long polling running in half an hour. The webhook will take 40-60 minutes because you need an HTTPS certificate. Handling the 429 error adds another 20 minutes. That's two to three hours of unhurried work with checks at every step.
Preparation: Tools and Access
Before writing a single line of code, let's gather everything you need. That way you won't have to stop mid-step to hunt down your proxy password or figure out how to install a library.
Required Tools and Access
- A Telegram account with a linked phone number. You'll use it to create a bot via the official BotFather.
- Proxy access: server address, port, login, and password. In your mobileproxy.space dashboard, these details are shown in the card for the proxy you bought. Usually two ports are available: one for HTTP and one for SOCKS5. Write down both.
- A computer or server with Python 3.10 or newer installed. For the webhook, you'll need a server with a public IP address and a domain; a home computer won't work for webhooks.
- The curl utility. It's already built into Windows 10 and 11, macOS, and Linux. Check with
curl --version. - A text editor: VS Code, Notepad++, or any other you're comfortable writing code in.
System Requirements
For polling, there are almost no requirements: any machine with internet access, including a laptop. The bot uses just a few dozen megabytes of memory. For a webhook, you need a VPS with a minimal configuration: 1 core, 1 GB of RAM, Ubuntu 22.04 or 24.04. You also need a domain pointing to the server's IP and port 443 open in the firewall.
What to Install
- Open your terminal.
- Check Python with
python3 --version. On Windows, the command might bepython --version. - Create a project folder:
mkdir tgbot-proxy, then go into it withcd tgbot-proxy. - Create a virtual environment:
python3 -m venv venv. Activate it: on Linux and macOSsource venv/bin/activate, on Windowsvenv\Scripts\activate. - Install the libraries:
pip install requests[socks] flask. The requests package handles requests to the Telegram Bot API, the socks extra is needed for SOCKS5 proxies, and Flask will receive the webhook.
Backups and Data Security
Treat your bot token like the password to your banking app. Anyone who gets it can message your customers as your bot. Create a .env file for the token and proxy details, and never push it to a public repository. If your bot is already running and you're moving it to a proxy, save the current configuration, export your database if you have one, and note the output of the getWebhookInfo method. Then rolling back takes just two minutes.
Tip: Create a separate test bot for experiments. Run every step of this guide on it first, and only move proven configurations to your production bot. That way you won't break conversations with real customers.
Core Concepts: How the Telegram Bot API Works
Let's break down the key terms in plain language. Without them, the next steps will look like magic; with them, everything becomes logical and predictable.
Telegram Bot API
It's just a plain HTTP interface. Your code sends a request to https://api.telegram.org/bot<TOKEN>/<method>, and Telegram's server responds with a JSON object. For example, the getMe method returns info about the bot, and sendMessage sends text to a chat. You don't need special libraries, curl or requests is enough. That's exactly why the Telegram Bot API is so easy to route through a proxy: it's the same traffic as any HTTPS website.
Updates
Every event for the bot—whether a message, button press, or being added to a group—arrives as an update object with a unique update_id. IDs increase in order. Your code's job is to fetch these objects and process them. There are exactly two ways to receive them, and they're mutually exclusive.
Long Polling
Your bot asks Telegram itself: are there any new updates for me? It does this using the getUpdates method. The word "long" means the server doesn't respond instantly with an empty list; instead, it keeps the connection open until the specified timeout—usually 30-60 seconds—and responds as soon as an event appears. This saves requests and gives near-instant reaction. Polling doesn't need a public address, works from behind a router, and works through any proxy. Ideal for starting out and for bots with light load.
Webhook
The opposite approach. You tell Telegram your server's address using the setWebhook method, and Telegram sends each update as a POST request to that address. You don't need to poll anything. Requirements: a public domain, an HTTPS certificate, and one of ports 443, 80, 88, or 8443. Webhooks scale better and don't waste resources waiting.
Important to understand: the proxy only affects your bot's outgoing requests to Telegram. Incoming webhook requests arrive directly at your server, and the proxy isn't part of that chain. So the phrase "webhook through a proxy" in practice means: the bot responds and calls API methods through the proxy, while it receives updates on its own public address.
429 Too Many Requests Error
Telegram limits request frequency. Guidelines for 2026: no more than one message per second to a single chat, no more than 20 messages per minute to a single group, and roughly 30 messages per second total per bot. When you exceed these, the server returns HTTP status 429 with a parameters.retry_after field in the response body indicating how many seconds to wait. You can't ignore it: retries without pauses actually increase the block duration.
Why Use a Proxy for a Bot Anyway
The reasons are purely practical. First, a fixed, predictable outbound IP: handy when you have many servers but need one external address. Second, traffic separation by project: each client or campaign gets its own channel. Third, corporate policies where all external connections must go through a gateway. Mobile proxies are valuable here for their stability and the ability to control IP changes on a schedule or via a link.
Step 1: Create a Bot and Get the Token
Goal: get an access token for the Telegram Bot API and confirm the bot exists.
- Open Telegram on your phone or computer.
- In the search bar, type BotFather. Choose the bot with the blue verification checkmark; fakes don't have one.
- Click Start or send the
/startcommand. You'll see a list of available commands. - Send the
/newbotcommand. - BotFather will ask for the bot's name. Enter a display name, like Proxy Test Bot. It can contain spaces and any characters.
- Next, it asks for a username. It must be unique, in Latin characters, and end with
bot, for exampleproxy_test_2026_bot. If it's taken, BotFather will tell you, so think of another. - You'll receive a congratulatory message with a string like
123456789:AAExampleTokenLettersAndDigits. This is your token. Copy it in full, including the digits before the colon. - Create a
.envfile in your project folder and add the lineBOT_TOKEN=your_token.
Warning: Never publish your token in chats, screenshots, or public repositories. If your token leaks, immediately send BotFather the /revoke command, select your bot, and get a new token. The old one stops working right away.
First Request Without a Proxy
Before complicating things, let's verify the token works with a simple request from your machine. Run this in your terminal, replacing the placeholder with your token:
curl https://api.telegram.org/bot123456789:AAExampleToken/getMeExpected response: JSON starting with {"ok":true,"result":{"id":123456789,"is_bot":true,.... Inside, you'll see the bot's username you just created.
Check: The response contains "ok":true and the correct username. If instead you got "error_code":401, the token was copied wrong; check that no characters were lost at the edges.
Possible Issues
- Username taken. Add numbers or a project abbreviation, just keep the bot suffix.
- BotFather not responding. Make sure you opened the verified bot with the checkmark, not a clone.
- 404 Not Found. You missed the word bot before the token. The format is strictly
/bot<TOKEN>/method.
Step 2: Connect the Proxy and Verify API Access
Goal: make requests to the Telegram Bot API go through your proxy and confirm this factually, not just in theory.
Understanding the Proxy Address Format
A proxy is described in one string: protocol://login:password@host:port. Grab the details from your dashboard. Say you got the host proxy-host, HTTP port 8080, SOCKS5 port 1080, login user, password pass. Then the strings look like this:
- HTTP proxy:
http://user:pass@proxy-host:8080 - SOCKS5 proxy:
socks5h://user:pass@proxy-host:1080
Note the letter h in socks5h. It means the domain name api.telegram.org will be resolved on the proxy side, not on your machine. This is the right choice for mobile proxies: DNS queries and traffic go through the same route. Without the h, requests will fail if your local DNS has issues, even if the proxy is fine.
If your password contains @, :, or /, you need to encode them: @ becomes %40, colon becomes %3A, slash becomes %2F. Otherwise the string won't parse correctly.
Verify with curl
- First, find out what IP external services see through your proxy. Run:
curl -x http://user:pass@proxy-host:8080 https://api.ipify.org. The response will be an IP address. It should be different from your home or server address. - Now the same request to the Telegram Bot API:
curl -x http://user:pass@proxy-host:8080 https://api.telegram.org/bot123456789:AAExampleToken/getMe. - For SOCKS5, change the parameter:
curl -x socks5h://user:pass@proxy-host:1080 https://api.telegram.org/bot123456789:AAExampleToken/getMe. - Measure the response time: add
-w "time: %{time_total}"at the end of the command. Values up to 1-2 seconds for a mobile proxy are normal.
Check: Both requests returned "ok":true, and the IP from the first command is different from your own. This means the proxy route works and the Telegram Bot API responds through it.
Configure the Proxy in Python
Create a config.py file with the following content, replacing the values with your own:
import os
TOKEN = os.getenv('BOT_TOKEN', '123456789:AAExampleTokenReplaceMe')
PROXY = os.getenv('BOT_PROXY', 'http://user:pass@proxy-host:8080')
PROXIES = {'http': PROXY, 'https': PROXY}
BASE = f'https://api.telegram.org/bot{TOKEN}'The https key in the dictionary is mandatory because the Telegram Bot API only works over HTTPS. A common beginner mistake is specifying only http and then wondering why traffic goes direct.
Now a test script check.py:
import requests
from config import PROXIES, BASE
ip = requests.get('https://api.ipify.org', proxies=PROXIES, timeout=15).text
print('Outbound IP:', ip)
me = requests.get(f'{BASE}/getMe', proxies=PROXIES, timeout=15).json()
print('Bot:', me['result']['username'])Run it: python check.py. The screen will show the proxy IP and the bot's username.
Tip: If you don't want to modify the code of a library you're already using, set the HTTPS_PROXY=http://user:pass@proxy-host:8080 environment variable. The requests library and most HTTP clients pick it up automatically. This is a convenient way to move an existing bot to a proxy without code changes.
Setup in Popular Frameworks
- aiogram 3.x: create a session
AiohttpSession(proxy='http://user:pass@proxy-host:8080')and pass it when creating theBot(token=TOKEN, session=session)object. For SOCKS5 you'll need the aiohttp-socks package. - python-telegram-bot 21.x: use
HTTPXRequest(proxy='http://user:pass@proxy-host:8080')and pass it toApplicationBuilder().token(TOKEN).request(request). - Node.js: the https-proxy-agent or socks-proxy-agent packages create an agent that you pass to HTTP client options or the bot library constructor.
Possible Issues
- 407 Proxy Authentication Required. Wrong login or password, or special characters weren't encoded. Double-check the dashboard details.
- Connection refused. Wrong port, or you mixed up HTTP and SOCKS5 ports. Try the other port.
- Request goes direct. No https key in the dictionary, or the environment variable was set in a different terminal window.
- SSL error. Don't disable certificate verification. Update the certifi package with
pip install -U certifiand check your system time.
Step 3: Run Long Polling Through the Proxy
Goal: get a working bot that fetches updates through the proxy using getUpdates and replies to messages without losing or duplicating them.
How to Build the Polling Loop Correctly
The logic is simple, but details matter. You call getUpdates with an offset equal to the last processed update_id plus one. That tells Telegram the previous updates were received and it removes them on its side. If you forget the offset, the same message keeps arriving and the bot starts replying multiple times.
The timeout parameter sets how many seconds the server keeps the connection open waiting for events. This is where proxy specifics come in. Every proxy has its own idle connection timeout; for mobile proxies it's often around 60-120 seconds. If the polling timeout is longer, the proxy will cut the connection before Telegram responds, and you'll get an error instead of updates. A safe value is timeout=50 with an HTTP client timeout of 60 seconds. Client timeout should always be greater than the polling timeout; otherwise the client drops first.
Writing the Bot
- Create a file
polling.py. - Copy the code below.
- Run it with
python polling.py. - Send your bot any message in Telegram.
import time, requests
from config import PROXIES, BASE
offset = 0
print('Polling started through proxy')
while True:
try:
r = requests.get(f'{BASE}/getUpdates', params={'offset': offset, 'timeout': 50}, proxies=PROXIES, timeout=60)
data = r.json()
except requests.RequestException as e:
print('Network error:', e)
time.sleep(3)
continue
if not data.get('ok'):
print('API error:', data)
time.sleep(3)
continue
for upd in data['result']:
offset = upd['update_id'] + 1
msg = upd.get('message')
if msg and 'text' in msg:
requests.post(f'{BASE}/sendMessage', json={'chat_id': msg['chat']['id'], 'text': 'Got it: ' + msg['text']}, proxies=PROXIES, timeout=15)Let's break down what's happening. The outer loop never exits on its own. The try block catches network errors: broken proxy, timeout, IP change. On error, the script waits three seconds and retries instead of crashing. The data.get('ok') check catches API-level errors. The inner loop processes each update and immediately advances the offset.
Check: The console shows a startup line, and after you send a message the bot replies "Got it: your text." Send three messages in a row; the bot should reply exactly once to each. Stop the script with Ctrl+C, send a message, then start it again: the bot will reply to the missed message because Telegram stores unprocessed updates for up to 24 hours.
Mobile Proxy Specifics for Polling
Mobile proxies can change their IP address: on a timer or via a special link from the dashboard. At the moment the IP changes, the open polling connection breaks. This isn't a bug in your code; it's normal network behavior. Our loop is already prepared for it: it catches the exception, waits, and continues from the same offset. No update will be lost.
Still, frequent rotation creates extra noise in the logs and micro-delays. Recommendation: for a polling bot, set the rotation interval in the dashboard to 10 minutes or more, or disable automatic rotation and change the IP via link only when truly needed. A bot isn't a scraper; it doesn't need a fresh address every minute.
Tip: Add the time and update_id of each update to your log. When someone says next week that the bot didn't reply, you can find in a minute whether the message reached your code or the problem was on the network side.
Possible Issues
- 409 Conflict error. The most common. A webhook is set for the bot, and polling and webhook can't work simultaneously. Run
curl -x your_proxy https://api.telegram.org/botTOKEN/deleteWebhookand restart the script. Another cause: two copies of the script are running; close the extra one. - Bot replies twice to each message. Offset isn't advancing, or two copies of the bot are running.
- Constant Read timed out. Polling timeout is greater than the proxy timeout. Reduce timeout to 30-40 seconds.
- 5-10 second reply delay. Check the proxy response time with curl. If the channel itself is slow, change the exit point or plan.
Step 4: Set Up a Webhook with HTTPS and a Secret Token
Goal: Telegram sends updates to your server itself, and the bot replies through the proxy. This step requires a VPS with a domain, so if you only have polling and it's working for you, you can jump to step 5 and come back later.
Preparing the Server and Domain
- Rent a VPS with Ubuntu. Note its public IP.
- In your domain control panel, create an A record, like
bot.example.com, pointing to the server's IP. Wait for DNS to update, usually 5-30 minutes. Verify withnslookup bot.example.com. - Connect to the server via SSH and update packages:
sudo apt update. - Install nginx and certbot:
sudo apt install nginx certbot python3-certbot-nginx. - Open ports 80 and 443:
sudo ufw allow 80,sudo ufw allow 443. - Get a free certificate:
sudo certbot --nginx -d bot.example.com. Follow the prompts, enter your email, agree to the terms. In a minute the certificate is issued.
Telegram only accepts webhooks over HTTPS with a valid certificate from a trusted authority. A certbot certificate meets this requirement. A self-signed certificate is also possible, but then you'd have to upload its public part via the certificate parameter in setWebhook, which adds hassle. For beginners, certbot is simpler.
Configuring nginx as the Entry Point
Nginx will receive HTTPS requests from Telegram and pass them to your Flask app listening on local port 8080. Open the site config created by certbot with sudo nano /etc/nginx/sites-available/default and add this location inside the server block with port 443:
location /tg/webhook {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_read_timeout 30s;
}Save the file, check the syntax with sudo nginx -t, and reload with sudo systemctl reload nginx.
Tip: Make the webhook path non-obvious, like /tg/webhook/k8s7d2f. This isn't a replacement for the secret token, but an extra layer: random scanners won't find your entry point.
Writing the Webhook Handler
Create a webhook.py file on the server. It accepts POSTs from Telegram, verifies the secret header, and replies to the user through the proxy. We'll write the call function in the next step; for now use a plain requests.post with proxies.
import requests
from flask import Flask, request, abort
from config import PROXIES, BASE
app = Flask(__name__)
SECRET = 'MySecret123'
@app.post('/tg/webhook')
def webhook():
if request.headers.get('X-Telegram-Bot-Api-Secret-Token') != SECRET:
abort(403)
update = request.get_json(silent=True) or {}
msg = update.get('message')
if msg and 'text' in msg:
requests.post(f'{BASE}/sendMessage', json={'chat_id': msg['chat']['id'], 'text': 'Got it via webhook'}, proxies=PROXIES, timeout=15)
return 'ok', 200
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080)The key point: the X-Telegram-Bot-Api-Secret-Token header. Telegram adds it to every request if you set secret_token when installing the webhook. Any request without the correct value is rejected with a 403. That's how no one can feed your bot fake messages.
Start the app: python webhook.py. For continuous operation, later set it up as a systemd service, but a direct run works for testing.
Registering the Webhook Through the Proxy
The setWebhook call itself also goes through the proxy, since it's an outgoing request to the Telegram Bot API. Run this on any machine with proxy access:
curl -x http://user:pass@proxy-host:8080 -F "url=https://bot.example.com/tg/webhook" -F "secret_token=MySecret123" -F "max_connections=40" -F "drop_pending_updates=true" https://api.telegram.org/bot123456789:AAExampleToken/setWebhookLet's break down the parameters. url — your handler's address. secret_token — a string of 1 to 256 characters using Latin letters, digits, hyphen, and underscore; it must match SECRET in your code. max_connections — how many simultaneous requests Telegram can open to you, from 1 to 100, default 40. drop_pending_updates — reset accumulated updates so the bot doesn't fire off a barrage of old replies when switching.
Expected response: {"ok":true,"result":true,"description":"Webhook was set"}.
Diagnostics with getWebhookInfo
This is the main webhook debugging tool. Run:
curl -x http://user:pass@proxy-host:8080 https://api.telegram.org/bot123456789:AAExampleToken/getWebhookInfoIn the response, look at these fields: url should match yours, pending_update_count shows the queue of unprocessed updates, and last_error_date and last_error_message appear if Telegram couldn't deliver an update. An empty error field after sending a test message means complete success.
Check: Message your bot. It replied "Got it via webhook." In getWebhookInfo there's no last_error_message, and pending_update_count is zero. In the Flask logs you see a POST /tg/webhook line with code 200.
Warning: The webhook handler must respond quickly, within a few seconds. If your code thinks too long—say, it makes a heavy database query or a slow external service call through a proxy—Telegram considers the delivery failed and retries. Move heavy work to a background task, and return 200 to the webhook immediately.
Possible Issues
- last_error_message: SSL error. The certificate isn't valid, expired, or issued for a different domain. Check
sudo certbot certificates. - Connection timed out. Port 443 is closed in the firewall or by the host. Check
sudo ufw statusand your VPS panel settings. - Wrong response from the webhook: 403. The secret token in your code doesn't match the one in setWebhook. Check letter case.
- Wrong response from the webhook: 502. Flask isn't running or is listening on a different port. Check
ss -tlnp | grep 8080. - Bad webhook: port not allowed. Use only 443, 80, 88, or 8443.
Step 5: Handle 429 Errors and Make Requests Robust
Goal: write one function for all Telegram Bot API calls that pauses on 429, retries on network and proxy failures, and never crashes the bot.
Why You Can't Ignore 429
When you broadcast to a thousand users, the 30 messages per second limit is hit instantly. Telegram responds with 429 and specifies retry_after. If you keep hammering the server, messages won't go out, and the wait time in subsequent responses will grow. When working through a proxy there's a nuance: Telegram limits are tied to the bot, not the IP. Changing the IP via rotation won't bypass the limit and shouldn't be used for that. The only correct path is to respect retry_after and control sending speed.
Writing a Universal Wrapper
Add the following function to config.py or a separate api.py:
import time, requests
from config import PROXIES, BASE
def call(method, payload, retries=6):
for attempt in range(retries):
try:
r = requests.post(f'{BASE}/{method}', json=payload, proxies=PROXIES, timeout=15)
except requests.RequestException as e:
print('Network or proxy:', e)
time.sleep(min(2 ** attempt, 30))
continue
if r.status_code == 429:
wait = r.json().get('parameters', {}).get('retry_after', 1)
print('429 Too Many Requests, waiting', wait, 'sec')
time.sleep(wait + 0.5)
continue
if 500 <= r.status_code < 600:
print('Telegram server error', r.status_code)
time.sleep(min(2 ** attempt, 30))
continue
data = r.json()
if not data.get('ok'):
print('API error:', data.get('description'))
return data
raise RuntimeError('Telegram Bot API: retries exhausted for ' + method)What this function does. On a network error, it waits exponentially: 1, 2, 4, 8 seconds, capped at 30. This is how the bot survives IP changes on the proxy or a brief channel failure. On 429, it reads retry_after, adds half a second of buffer, and retries. On 5xx errors on Telegram's side, it also retries with a pause. On 400 or 403 errors—like when a user blocked the bot—retrying is pointless, so the function returns the response as is, and you decide what to do next.
Rate-Limit Ahead of Time
It's better to never hit 429 at all. For broadcasts, add a pause between messages. The simplest option: time.sleep(0.05) after each send gives 20 messages per second, below the overall limit. For a single chat, keep pauses of at least one second. For groups, no more than 20 messages per minute.
def broadcast(chat_ids, text):
sent, failed = 0, 0
for cid in chat_ids:
res = call('sendMessage', {'chat_id': cid, 'text': text})
if res.get('ok'):
sent += 1
else:
failed += 1
time.sleep(0.05)
print('Sent:', sent, 'Failed:', failed)Tip: Store responses with the 403 Forbidden: bot was blocked by the user error. Remove such users from your broadcast list. This reduces load and protects against extra limits, since failed requests also count.
Testing 429 Handling
- Create a test group and add the bot.
- Run a loop of 40 calls to
call('sendMessage', {...})to that chat without pauses. - Watch the console: after a few fast sends, you'll see a line about 429 and the wait time.
- Verify that after the pause, sending continues and all 40 messages arrive.
Check: All messages delivered, the bot didn't crash with an exception, and the log shows retry_after pauses. Replace requests.post in polling.py and webhook.py with the call function so all Telegram Bot API work goes through one protected channel.
Possible Issues
- Huge retry_after, hundreds of seconds. You ignored the limit for too long. Stop sending completely, wait the specified time, and lower your rate.
- 429 on the very first messages. Another process is using the same token. Check if an old bot version is running.
- Non-JSON response on 429. Some proxies return their own HTML error page. Wrap r.json() in a try and on failure wait a fixed 5 seconds.
Step 6: Advanced Setup for High-Load Projects
Goal: prepare your bot for growth: multiple bots through multiple proxies, a send queue, a local Bot API server, and smart rotation. Beginners can skip this section and come back when they have more than a thousand users.
Multiple Bots Through Different Proxies
Marketers often run a dozen bots for different projects from one server. The right architecture: each bot gets its own proxies dictionary and its own requests.Session(). A session reuses TCP connections, which reduces proxy load and speeds up requests 2-3 times. Store config as a list: token, proxy address, update mode. One process per bot is easier to debug than one process for all.
Send Queue Instead of Direct Calls
For broadcasts to 10,000+ users, direct calls from your main code stop working. Set up a queue: Redis or at least a database table with fields chat_id, text, status, attempts. A separate worker picks up jobs and calls the call function with speed control. On 429, the worker sleeps instead of blocking webhook receipt. This way the bot keeps responding to user commands while the broadcast runs in the background. As of 2026, the Telegram Bot API allows paid bots to raise the limit to 1000 messages per second via the allow_paid_broadcast parameter, but that costs Stars, so for most projects a queue at 20-25 messages per second is still the optimal choice.
Local Bot API Server
Telegram publishes the Bot API server source code, which you can run yourself. Your code then talks to localhost instead of api.telegram.org, and the local server communicates with Telegram. Pros: file uploads up to 2 GB instead of 50 MB, webhooks on any port without HTTPS inside your network, lifting of certain limits. Cons: the proxy must be configured at the local server level, not in your bot's code. A choice for teams with DevOps skills, overkill for starting out.
IP Rotation and Webhooks
With webhooks, IP rotation on the proxy is almost invisible: each sendMessage call is short, and a connection break at the moment of change affects at most one request, which the call wrapper retries. That's why webhooks can tolerate more frequent rotation than polling. Still, frequent changes make little sense: the Telegram Bot API doesn't limit requests by IP, and limits are tied to the token. Set rotation for your infrastructure reasons, not for the API's sake.
Health Monitoring Without a Separate System
The minimal set to start with right away: every minute, call getWebhookInfo and log pending_update_count and last_error_message. If the queue grows for three checks in a row, your handler can't keep up. For polling, record the time of the last successful getUpdates: if more than two minutes have passed, the connection is stuck—restart the process. That's enough to learn about problems before your clients do.
Tip: Log not just errors but the time of each request to the Telegram Bot API through the proxy. A slow increase in average response time from 0.3 to 2 seconds will warn you about channel degradation days before the bot starts losing messages.
Verifying the Result: A Checklist for a Finished Solution
Go through the list. Every item must be checked off; only then is the bot ready for real users.
Checklist
- A curl command with the -x flag through the proxy returns
"ok":truefor the getMe method. - The check.py script shows the proxy IP, not your own.
- Polling replies to a message once, with no duplicates.
- After stopping and restarting polling, missed messages are processed.
- When you force an IP change on the proxy, polling recovers on its own within 3-5 seconds.
- Webhook: getWebhookInfo shows your url, zero pending_update_count, and an empty last_error_message.
- A request to the webhook without the secret header gets a 403.
- A fast send of 40 messages to one chat completes without crashing, and retry_after pauses appear in the log.
- The token and proxy details are in .env, not in the code.
How to Test End-to-End
- Start the bot in your chosen mode.
- Send it two messages from three different accounts.
- Confirm six replies, one per message.
- Click the IP-change link in the proxy dashboard and immediately send another message.
- The bot should reply within 10 seconds.
- Run the 429 test from step 5.
- Check the logs: no unhandled exceptions or stack traces.
Success Metrics
Bot reply time to a message under 2 seconds. Failed request rate to the Telegram Bot API after all retries below 0.1 percent. Zero duplicates over a day of operation. pending_update_count in getWebhookInfo doesn't exceed ten during peak hours. If your metrics match, congratulations: you've built a reliable setup that will last for many months.
Common Mistakes and Their Solutions
409 Conflict Error on getUpdates
Cause: a webhook is set for the bot, or two polling instances are running. Solution: call deleteWebhook through the proxy, make sure only one process is running. Check with ps aux | grep polling.
Bot Replies Twice to Each Message
Cause: offset isn't incrementing, or two bot copies are running on different servers with the same token. Solution: check the offset = update_id + 1 line, stop extra copies. For webhooks, make sure nginx isn't duplicating requests to two backends.
407 Proxy Authentication Required Error
Cause: wrong proxy credentials or unencoded special characters in the password. Solution: recheck the login and password in the dashboard, encode @, :, and / in the password, try IP authentication if your plan supports it.
Read Timed Out Every 50-60 Seconds
Cause: polling timeout is greater than or equal to the proxy idle timeout. Solution: reduce the timeout in getUpdates to 30-40 seconds, and keep the client timeout 10 seconds higher.
Webhook Set but Updates Don't Arrive
Cause: port closed, invalid certificate, or handler not responding with 200. Solution: check last_error_message in getWebhookInfo—it's a precise diagnosis. Test with curl -I https://bot.example.com/tg/webhook from another computer.
Wrong Response from the Webhook: 403 Forbidden
Cause: the secret token in the code differs from the one passed in setWebhook. Solution: reinstall the webhook with the same value as in the code. Remember that setWebhook with new parameters completely replaces the previous ones.
Constant 429s Under Light Load
Cause: the bot sends to one chat more than once per second, like several messages in a row in response to one command. Solution: combine replies into one message, use editMessageText instead of new messages for progress, add a pause between sends to the same chat_id.
Traffic Goes Direct, Bypassing the Proxy
Cause: no https key in the proxies dictionary, environment variable not visible to the process, framework didn't pick up the setting. Solution: check the outbound IP with an api.ipify.org request inside the same code you send messages with. Don't trust assumptions—verify factually.
SSL: CERTIFICATE_VERIFY_FAILED on Requests Through the Proxy
Cause: outdated root certificate set or incorrect system time. Solution: update certifi, sync the time. Never disable certificate verification with verify=False: it opens the door to traffic substitution involving your token.
FAQ: Common Setup Questions
Which to choose first: polling or webhook?
Polling. It works from anywhere, doesn't need a domain or certificate, and you can switch to a webhook later in an hour. A webhook makes sense when your bot serves thousands of users or runs on a server that already has HTTPS.
Can I use one proxy for multiple bots?
Yes. Telegram Bot API limits are tied to the bot's token, not the IP. Ten bots through one proxy don't interfere with each other from the API's perspective. The only limit is the proxy's bandwidth, which is more than enough for text bots.
Do I have to route both the webhook and replies through the proxy?
Incoming webhook requests arrive directly at your server; routing them through a proxy is impossible by definition. Only outgoing calls go through the proxy: sendMessage, setWebhook, getFile, and other methods. This is the normal and only possible setup.
How can I tell requests actually go through the proxy?
Inside the same code, request https://api.ipify.org with the same proxies setting and compare it with your real IP. You can also check the traffic stats in your proxy dashboard: the counter should increase while the bot runs.
What to do when the proxy changes IP while the bot is running?
Nothing, if you used the code from this guide. Polling catches the connection break and resumes from the saved offset. The call function retries the failed request. The only recommendation: don't set rotation more often than every few minutes for polling mode.
SOCKS5 or HTTP proxy for the Telegram Bot API?
For a bot, there's almost no difference; both work. HTTP proxies are simpler to configure and supported by all clients without extra packages. SOCKS5 is more convenient when you want to guarantee DNS resolution on the proxy side using the socks5h scheme. Choose what you already use in other projects.
How many messages can I send without hitting 429?
Stick to these guidelines: up to 1 message per second to a private chat, up to 20 per minute to a group, up to 25-30 per second overall. For mass broadcasts, aim for 20 messages per second and always handle retry_after, because limits can temporarily drop under high load on Telegram.
How to safely move a running bot to a proxy?
First, test the proxy with curl on getMe. Then add the HTTPS_PROXY environment variable or configure proxies in the code, and run the bot with a test token. Only after successful checks, switch the production token. Keep the old configuration handy for rollback.
How to roll back the webhook and return to polling?
One deleteWebhook call through the proxy. If you want to keep accumulated updates for polling, don't pass drop_pending_updates. Then start polling.py, and the bot will pick up the queue.
Can I set multiple webhook URLs for one bot?
No, a bot has exactly one webhook. If you need to distribute load, put an nginx load balancer behind the single URL to spread requests across multiple app instances. To Telegram, it looks like one entry point.
Conclusion
Let's sum up what you've done. You created a bot and got a token, learned to test the proxy via curl and Python, and confirmed that traffic to the Telegram Bot API goes through the right route. You built a robust long polling loop that survives IP changes and doesn't duplicate messages. You set up a webhook with a real HTTPS certificate and protected it with a secret token. And most valuable: you wrote one function that respects retry_after on 429, retries requests on network failures, and turns a finicky channel into a reliable one.
What to do next. Set up the bot as a systemd service so it starts after a server reboot. Move tokens and proxy addresses to environment variables on the production machine. Add simple response-time logging and review it once a week. If your user base grows large, move to the send queue from the advanced section.
Where to grow. Explore Telegram Bot API methods for inline buttons, payments, and mini apps—they open up entirely new scenarios for marketing and sales. Learn async frameworks like aiogram or python-telegram-bot; they'll handle the polling and retry routine for you, and you already understand what's happening under the hood. And most importantly: don't be afraid to experiment on a test bot. Every 429 or 409 error you catch and fix yourself makes you stronger than any ready-made template. You've got this.