⌘K
Rate limits
The TimeTracking API allows 600 requests per minute per API key; how the window works, what a 429 looks like, and how to stay under the limit with batch endpoints and pagination.
Last updated: · Markdown version
| Limit | Value |
|---|---|
| Requests per API key | 600 per minute |
| Window | Fixed calendar minute (UTC), counted per key; resets at hh:mm:00 |
| Applies to | Requests authenticated with an API key (tt_live_…) |
| Batch size | 500 items per batch request |
| Page size | limit 1–500 (default 100) |
| Other limits | Webhook delivery timeout 10 s; inbound email: max 10 attachments, 15 MB each, XLSX ≤ 5 MB and ≤ 20,000 rows; free orgs: 25 inbound emails per day |
When you exceed it
HTTP/1.1 429 Too Many Requests
retry-after: 60
content-type: application/json
{"error":{"code":"rate_limited","message":"Rate limit of 600 requests per minute exceeded"}}
Retry-After is always 60 seconds. Requests that hit the limit still count. Wait for Retry-After, or until the next calendar minute, before retrying.
Staying under the limit
- Batch writes.
POST /people/batch,/clients/batch,/placements/batchand/claims/batchtake up to 500 rows in one request and report results per row. - Paginate with large pages.
GET /days?from=…&to=…&limit=500. Useupdated_sincefor incremental syncs instead of re-reading everything (see pagination). - Use webhooks instead of polling. Fetch details only when an event arrives (see webhooks).
- Use one key per integration. Each key has its own budget.
- Spread scheduled jobs. Avoid starting every cron job at
:00.
Retry pattern
import time, requests
def call(method, url, **kw):
for attempt in range(6):
r = requests.request(method, url, timeout=30, **kw)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", "60")))
continue
if r.status_code >= 500:
time.sleep(min(2 ** attempt, 30))
continue # pass the same Idempotency-Key on writes
return r
raise RuntimeError("gave up")
Planned: a dedicated bulk export feed for BI, so you do not need high-volume polling.