Errors
All failures use a consistent JSON envelope so clients can branch on error.code and show error.message to developers (and map to user-friendly copy in your product).
Error envelope
{
"error": {
"code": "validation_error",
"message": "Invalid query parameters.",
"details": {
"formErrors": [],
"fieldErrors": { "limit": ["Too big"] }
}
}
}details is optional. It may include Zod flatten output, required scopes, or other structured context. Do not depend on undocumented detail shapes for critical control flow.
Codes
| Code | HTTP | Meaning |
|---|---|---|
validation_error | 400 | Bad query, path, or body |
unauthorized | 401 | Missing, invalid, revoked, or expired API key |
forbidden | 403 | Valid key but missing scope or not allowed |
not_found | 404 | Resource missing or not visible |
rate_limited | 429 | Soft rate limit exceeded — retry after reset |
service_unavailable | 503 | DB/config down, or key auth misconfigured (e.g. missing service role) |
database_error | 500 | Query failed unexpectedly |
internal_error | 500 | Unhandled server error |
Rate limit headers
When rate limiting runs, responses include:
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset(unix seconds)
Approximate defaults today: anonymous ~120/min, keyed ~600/min (in-memory soft stub — not a hard multi-region guarantee).
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and retry after the reset window.",
"details": { "reset_at": 1721300000 }
}
}CORS
API responses send Access-Control-Allow-Origin: * and allow GET, POST, PATCH, DELETE, OPTIONS with headers Authorization, Content-Type. OPTIONS returns 204 for preflight. Prefer server-side calls with secrets — never ship API keys in public browser bundles.
Example: invalid listing id
curl -sS -i "https://www.threadzsocial.com/api/v1/listings/not-a-uuid"{
"error": {
"code": "validation_error",
"message": "Invalid listing id. Expected a UUID."
}
}Example: missing key on /me
curl -sS -i https://www.threadzsocial.com/api/v1/me{
"error": {
"code": "unauthorized",
"message": "API key required. Pass Authorization: Bearer <key>."
}
}Client guidance
- Treat
5xxand network errors as retryable with backoff. - Do not retry
401/403without fixing the key or scopes. - On
429, wait untilX-RateLimit-Reset(ordetails.reset_at). - Log
codeclient-side; never log full API keys.
Auth details: Authentication. Changelog: Changelog.