• Base URL https://terrads.com/api/v1
  • Updated September 10, 2026

Requests 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.

TierEndpointsLimit
WritesPOST /runs, answer, approve-copy, cancel30 per minute
ReadsGET /runs, GET /runs/{runId}, GET /runs/{runId}/results120 per minute
Catalogbrands, products, angles, audiences, models60 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:

HeaderMeaning
X-RateLimit-LimitThe limit of the bucket that refused you.
X-RateLimit-RemainingRequests left in that bucket — 0 on a refusal.
X-RateLimit-ResetWhen the bucket resets, as a Unix timestamp in seconds.
Retry-AfterSeconds 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.
  • 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.