Errors, Rate Limits & Pagination
Error response shape
All errors use the same envelope:
{
"error": {
"code": "validation_error",
"message": "site_id is required"
}
}
The HTTP status code complements the code field — the code is stable for programmatic handling; the message is human-readable and may change.
Error codes
| HTTP | Code | When |
|---|---|---|
400 |
validation_error |
Required field missing or invalid (e.g. unknown status value, no body fields to update) |
401 |
unauthorized |
Missing/malformed Authorization header, key not found, key deactivated, or workspace api_enabled=false |
404 |
not_found |
Resource doesn't exist or belongs to another workspace. No endpoint matches the URL |
429 |
rate_limit_exceeded |
More than 60 requests/min on this key. See headers below |
500 |
internal_error |
Unhandled server error — report to [email protected] with the request ID |
502/504 |
upstream_error |
Downstream service (idea generator, experiment engine) failed. Safe to retry with backoff |
Rate limits
Budget: 60 requests per minute per API key (sliding window).
Response headers (always present):
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
60 (your per-minute budget) |
X-RateLimit-Remaining |
Requests left in the current window |
X-RateLimit-Reset |
Unix timestamp (seconds) when the window resets |
On 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1714147237
{"error":{"code":"rate_limit_exceeded","message":"Rate limit exceeded"}}
Recovery: sleep for Retry-After seconds, then resume. If you need sustained higher throughput, contact support about a custom limit.
Retry strategy
Treat these as retryable with exponential backoff + jitter:
429— respectRetry-After500,502,503,504— retry up to 3 times, starting at 1s, doubling each attempt- Connection errors (timeouts, TCP resets)
Treat as not retryable:
400,401,404— fix the request, don't retry402— not used by the REST API (it's reserved for the agent gateway's insufficient-credits case)
Pagination
List endpoints (/v1/ideas, /v1/experiments) use offset pagination:
| Param | Type | Default | Max |
|---|---|---|---|
limit |
integer | 20 | 100 |
offset |
integer | 0 | — |
Results are always sorted created_at DESC. To paginate:
# Page 1
curl "https://api.abtestbot.com/v1/ideas?site_id=<uuid>&limit=50&offset=0"
# Page 2
curl "https://api.abtestbot.com/v1/ideas?site_id=<uuid>&limit=50&offset=50"
When data.length < limit, you've reached the end.
CORS
The API is CORS-enabled for browser usage:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Warning: this means raw API keys can technically be used from a browser, but you should never ship sk_live_ keys to client-side code. Proxy through your own backend.