Failed responses usually look like { "status": "ERROR", "error": "..." }:
{ "status": "ERROR", "error": "Daily call limit of 50000 reached. Resets at 00:00 UTC, or upgrade your plan" }Exception: requests beyond your plan's history window (e.g. Starter asking for bars older than 5 years) return 403 with status NOT_AUTHORIZED.
Always branch on the HTTP status code. Error messages are in English for accounts created on the English site.
- 401 — API key missing or invalid.
- 403 — key disabled or expired (stocks and options are checked separately), product not purchased (e.g. an
O:options symbol without an Options plan), outside your history window, or a feature your plan lacks (e.g. Flat Files on a trial). - 404 — the endpoint isn't in your plan (e.g. an Advanced-only endpoint with a Starter key).
- 429 — rate, concurrency, daily call, monthly traffic or Flat Files limit exceeded. Respect
Retry-After. - 502 / 503 — the upstream source is temporarily unavailable (cached history may still be served).
- 504 — the upstream timed out. Retry later and lower concurrency for bulk jobs.
WebSocket errors aren't HTTP codes; they arrive as {"ev":"status","status":"auth_failed"|"error","message":"..."}.