Appearance
Are you an LLM? You can read better optimized documentation at /api/rate-limiting.md for this page in Markdown format
Rate Limiting
Limits
| Resource | Limit |
|---|---|
| REST endpoints | 30 requests per minute, per key |
| SSE stream | 1 concurrent connection per key |
Limits are per API key. Multiple keys on the same account each have their own independent limit.
REST — 429 responses
When you exceed 30 requests per minute, the API returns 429:
json
{
"type": "https://telegramtometatrader.com/problems/rate-limited",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded. Retry after 12 seconds."
}The Retry-After response header tells you how many seconds to wait:
Retry-After: 12Every response also carries X-RateLimit-Remaining so you can see headroom before hitting the limit:
X-RateLimit-Remaining: 24SSE — concurrent stream limit
Only one live stream connection per API key at a time. Opening a second connection while one is already active returns 409:
json
{
"type": "https://telegramtometatrader.com/problems/conflict",
"title": "Conflict",
"status": 409,
"detail": "A stream is already open for this API key."
}The stream slot is released when the existing connection closes (client disconnect, 5-minute recycle, or auth revoke). If you get a 409 unexpectedly, wait a few seconds for the previous connection to fully close, then reconnect.
INFO
The SSE endpoint does not count against the 30 req/min REST limit while the stream is connected.
What counts
Every GET /api/v1/* request (except /stream while connected) counts toward the per-minute window. The window resets on deploy, so treat the limit as a soft ceiling rather than a precise quota.
Backfill pattern
When paginating through historical data, space your requests to stay under the limit:
python
import time, requests
BASE = "https://app.telegramtometatrader.com/api/v1"
headers = {"Authorization": "Bearer ttmt_live_…"}
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["after"] = cursor
resp = requests.get(f"{BASE}/trades", params=params, headers=headers)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 60))
time.sleep(wait)
continue # retry same page
resp.raise_for_status()
page = resp.json()
process(page["data"])
if not page["has_more"]:
break
cursor = page["next_cursor"]
time.sleep(0.5) # ~2 req/s — well under the 30/min limit
