
HTTP status codes, error response format, rate limits, and limit-reached responses.
All errors use the same envelope shape:
{
"data": null,
"error": "Human-readable error message",
"meta": { /* optional error detail */ }
}| Status | Meaning |
|---|---|
400 | Bad request — invalid query parameters or request body |
401 | Unauthorized — missing, invalid, or revoked API key |
402 | Limit reached — org has hit a count-based plan limit |
403 | Forbidden — org or user is banned |
404 | Not found — resource doesn't exist or isn't in your org |
429 | Rate limited — daily API quota or per-key sliding-window exceeded |
500 | Server error |
Returned when a write operation would exceed a count-based plan limit. The meta field contains details:
{
"data": null,
"error": "Limit reached: max_creators_per_org",
"meta": {
"code": "limit_reached",
"limitKey": "max_creators_per_org",
"current": 5,
"max": 5
}
}See Limits & plan tiers for all limit keys.
Two separate systems can produce 429s:
Daily quota — your org's max_api_requests_per_day was exhausted. Resets at UTC midnight.
{
"data": null,
"error": "Rate limit exceeded",
"meta": {
"code": "rate_limit_exceeded",
"retryAfter": 3421
}
}retryAfter is seconds until the daily counter resets.
Per-key sliding window — too many requests in a short window from a single API key. Retry after a brief delay.
{
"data": null,
"error": "Too many requests",
"meta": {
"code": "too_many_requests"
}
}Always check meta
The meta field on error responses contains structured detail — use meta.code for programmatic error handling rather than parsing the error string.
const res = await fetch('https://www.trackagoat.com/api/v1/creators', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
if (!res.ok) {
if (res.status === 429) {
const retryAfter = body.meta?.retryAfter ?? 60;
// wait retryAfter seconds and retry
}