Rate limits
https://terrads.com/api/v1Updated September 10, 2026Requests are limited per API key and per team, in one-minute windows. Both limits must have room, and the team limit is the same figure as the per-key limit: a team's writes total 30 per minute across all its keys, however many keys it holds.
| Tier | Endpoints | Limit |
|---|---|---|
| Writes | POST /runs, answer, approve-copy, cancel | 30 per minute |
| Reads | GET /runs, GET /runs/{runId}, GET /runs/{runId}/results | 120 per minute |
| Catalog | brands, products, angles, audiences, models | 60 per minute |
Requests with an invalid key are limited too, so a bad credential cannot be used to probe the API at speed.
When you are over
The response is 429 with code rate_limited and these headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The limit of the bucket that refused you. |
X-RateLimit-Remaining | Requests left in that bucket — 0 on a refusal. |
X-RateLimit-Reset | When the bucket resets, as a Unix timestamp in seconds. |
Retry-After | Seconds to wait before retrying. At least 1. |
Wait for Retry-After and resend. The refused request did nothing — a
refused POST /runs created nothing and charged nothing, and its
Idempotency-Key is untouched.
If the rate limiter itself is unavailable, requests are refused with 429
rather than allowed through unlimited. Treat it like any other 429.
Polling without hitting the reads limit
A run is polled with GET /runs/{runId}. Polling one run every few seconds
is well inside the reads budget; polling many runs in tight loops is not.
Poll no faster than every few seconds, back off while a run is queued, and
stop polling once status is terminal. Use ?after= so each poll costs the
same whatever the run's history.
Fair-use caps
Some 429s are not about request rate and carry no X-RateLimit-*
headers. They are the same fair-use rules the app applies:
too_many_active_runs— the team already has as many runs in progress as it may. Wait for one to finish before creating another.model_concurrency_cap— too much work in flight on one model. Wait, or spread runs across models.daily_limit_reached— the team's credit ceiling for the rolling day. Try again later.too_many_pending_generations— too much unfinished work queued. Let it drain.limit_reached— a plan limit on the number of something (brands, for example).
None of these charge anything. The thresholds vary by plan and are not part of the API contract.