Errors

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

Body
{
  "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

CodeHTTPMeaning
validation_error400Bad query, path, or body
unauthorized401Missing, invalid, revoked, or expired API key
forbidden403Valid key but missing scope or not allowed
not_found404Resource missing or not visible
rate_limited429Soft rate limit exceeded — retry after reset
service_unavailable503DB/config down, or key auth misconfigured (e.g. missing service role)
database_error500Query failed unexpectedly
internal_error500Unhandled server error

Rate limit headers

When rate limiting runs, responses include:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset (unix seconds)

Approximate defaults today: anonymous ~120/min, keyed ~600/min (in-memory soft stub — not a hard multi-region guarantee).

429 example
{
  "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
curl -sS -i "https://www.threadzsocial.com/api/v1/listings/not-a-uuid"
Body
{
  "error": {
    "code": "validation_error",
    "message": "Invalid listing id. Expected a UUID."
  }
}

Example: missing key on /me

curl
curl -sS -i https://www.threadzsocial.com/api/v1/me
Body
{
  "error": {
    "code": "unauthorized",
    "message": "API key required. Pass Authorization: Bearer <key>."
  }
}

Client guidance

  • Treat 5xx and network errors as retryable with backoff.
  • Do not retry 401 / 403 without fixing the key or scopes.
  • On 429, wait until X-RateLimit-Reset (or details.reset_at).
  • Log code client-side; never log full API keys.

Auth details: Authentication. Changelog: Changelog.