Heads up During early access these endpoints are served through MCPconnect your agent in two minutes. Direct REST calls are available on the Unlimited plan: email us.

Error handling

Every error response follows the same shape. Branch on the code slug, never on the prose error field.

Every non-2xx JSON response uses the same envelope: a human-readable message you can surface to end-users, a stable machine-readable slug for switch statements, an HTTP status mirror, and optional context fields. The slug is the contract; the prose is not — we may rewrite the prose for clarity without bumping any version.

The error envelope

{
  "error": "Human-readable message; safe to surface.",
  "code": "machine_readable_slug",
  "http_status": 403,
  "...": "optional extras (e.g. current_tier, required_tier, ticker)"
}

How to use it

  • Branch on code. The slug list below is stable; the prose can change.
  • Surface error. It is written for end-users — never raw stack traces or implementation detail.
  • Trust http_status. It always matches the HTTP response status code; useful when your transport hides it.
  • Look for extras. 403 upgrade_required carries current_tier and required_tier; 404 ticker_not_found carries the offending ticker.

Request ID for support

Every response — success or error — includes an X-Request-ID header. When opening a support issue for a 500 internal_error, include that ID and we can locate the trace within seconds. We retain request logs for 30 days.

Complete code list

HTTPSlugWhen
400bad_requestMissing or malformed query parameter. The error field names the offending parameter.
400invalid_emailEmail field failed RFC 5322 validation.
401missing_api_keyNo X-API-Key header and no api_key query parameter.
401invalid_api_keyKey not recognized. Could be revoked, never existed, or has a typo.
401not_authenticatedBrowser session cookie is missing or expired. Used on dashboard-facing endpoints (/auth/web/*, /billing/usage, /billing/invoices).
403upgrade_requiredCaller's plan is below the endpoint's minimum tier. Payload includes current_tier and required_tier.
404ticker_not_foundTicker is not in our coverage. Could be delisted, foreign ADR, or simply not yet ingested.
404not_foundGeneric 404 — usually an unknown plan slug in /billing/checkout/{plan}.
409email_already_registeredAn account with this email already exists. Sign in instead.
429rate_limit_exceededPer-minute or daily quota hit. Retry after the window resets — the error field hints the next retry time.
500internal_errorServer bug. Include the X-Request-ID header value when reporting.
503internal_errorA dependency (DB, LemonSqueezy, etc.) is unavailable. Same shape as 500 but transient — retry with backoff.

Retry semantics

  • 4xx are caller errors — fix the request, don't retry blindly.
  • 429 is throttling — back off (1s, then 5s, then 30s) and resume.
  • 5xx are server-side and usually transient — exponential backoff with jitter, cap at 3 retries.
Idempotency. All GET endpoints are idempotent. POST /auth/register is not — a retry will fail with 409 if the first call succeeded. Check whether you received the API key in the failed response before retrying.