Guide
Retries and errors
How the API tells you something went wrong, and how your bot should respond.
The error envelope
Every failure answers { ok: false, error } with a matching HTTP status:
| Status | Meaning | What to do |
|---|---|---|
| 400 | Bad input | Fix the request; retrying unchanged will fail the same way |
| 401 | Bad or missing token | Check the token; it may have been rotated |
| 403 | Forbidden | Bot blocked, suspended, deleted, or wrong environment |
| 404 | Unknown id | The bot, chat, or message does not exist (or is not yours) |
| 429 | Rate limited | Back off and retry; see below |
403 on
sendMessage most often means one of three things: the user blocked your bot, the bot was suspended after abuse reports, or you used a test token against a live chat (or vice versa). The error message tells you which.Retrying 429s
Back off exponentially with jitter: wait 1s, then 2s, 4s, 8s, up to a minute. Never retry a 400, and never retry in a tight loop; hammering a 429 turns a brief slowdown into a long one.
Python
import random, time
import requests
def call(method, payload, tries=5):
for i in range(tries):
r = requests.post(f"{BASE}/{method}", headers=H, json=payload)
if r.status_code != 429:
return r.json()
time.sleep(min(60, 2 ** i) + random.random())
raise RuntimeError("rate limited too long")Polling without gaps or duplicates
Updates are an ordered per-bot sequence. Process a batch, then set offset to the highest update_id + 1 before the next call. If your bot crashes between processing and confirming, you will see the batch again, so make your handlers idempotent (key side effects on update_id).