You judge an API by its failure modes, because those are the ones you have to write code against.
Every error we return carries a stable reason
string you can branch on, so you never have to pattern-match an English sentence that might get
reworded. Here is every one of them.
Errors are JSON, always. The reason is the machine-readable part and
it is what you switch on. error is for your logs and for humans. Where a retry could
help, retry: true says so and a Retry-After header tells you how long to
wait. Where a request named something we do not have, the response names what we do have
rather than leaving you to guess.
Branch on reason, not on status alone.
Three different things return 402 and they want three different responses from your code: one is
“buy the product”, one is “you bought the wrong rung”, and one is
“you bought the dashboard, not the API”.
Who you are, and what that buys.
| Reason | Status | What it means & what to do |
|---|---|---|
no_key | 401 | No key was sent, or it was not a NoVo key. Send Authorization: Bearer novo_… or x-novo-api-key. Not retryable. |
no_api_subscription | 402 | The key is valid; the email behind it has no API subscription. The key authenticates, the subscription entitles — these are separate on purpose. Not retryable without buying. |
no_crypto_subscription | 402 | A dashboard door asked for a Crypto plan and did not find one. Distinct from the API SKU: a dashboard subscription does not open the API, and an API subscription does not open the dashboard. |
rung_too_low | 402 | Subscribed, but this surface sits on a higher rung than yours. Upgrade rather than retry. |
entitlement_unavailable | 503 | We could not check your entitlement — billing lookup failed. This is our fault, not a refusal. Retry after the Retry-After window; do not treat it as a downgrade. |
requires_ops_credential | 403 | An operator-only surface. No customer key opens it, so do not build against it. |
Ceilings are per key and per hour. Both carry Retry-After.
| Reason | Status | What it means & what to do |
|---|---|---|
key_hourly_limit | 429 | The hourly ceiling for this key is spent. Back off to the Retry-After value. The ceiling is a property of your rung, so persistent 429s mean upgrade, not tighter loops. |
mint_daily_limit | 429 | Too many key mints in a day from one account. You very likely already have a working key — fetch it rather than minting another. |
Something about the request itself. Every one of these names the valid set, so the fix is in the response.
| Reason | Status | What it means & what to do |
|---|---|---|
bad_ticker | 400 | Not a covered ticker. The body carries available with the full list. |
bad_axis | 400 | Unknown sort axis on the crypto screen. The body names the axes we rank on. |
bad_layer | 400 | Unknown layer. Valid values come back in the body. |
bad_lookback | 400 | lookback_minutes was not a number. Omit it entirely to get the whole Eastern trading day, which is the honest default. |
bad_request | 400 | The executor rejected the arguments. The error field carries the specific complaint. |
bad_email | 400 | Key minting needs a well-formed email — the same one that holds the subscription. |
no_coin | 400 | A coin argument is required and was missing. |
no_symbol | 400 | A symbol is required. The body carries an example. |
short_query | 400 | The search query was too short to rank meaningfully. Two characters cannot separate 1,200 articles. |
unknown_plan | 400 | Checkout was handed a plan name we do not sell. |
We understood you and we do not have it. Distinct from “we have it and it is empty”, which is a 200 — see how we handle missing data.
| Reason | Status | What it means & what to do |
|---|---|---|
unknown_coin | 404 | Not a coin on our book. The ~90-coin universe is published on /api/crypto-free. |
ticker_not_covered | 404 | A real ticker, not one we map. We map SPY, QQQ and IWM on the equity side. |
not_found | 404 | No record for that symbol in this dataset. |
Every code here means the failure is on our side. All are retryable and all carry Retry-After.
| Reason | Status | What it means & what to do |
|---|---|---|
collector_quiet | 503 | A collector stopped publishing. We return 503 rather than an empty book on purpose — a 200 with zeroes would render a dead publisher as a calm market, and those are not the same trade. Retry. |
upstream | 503 | A dependency we read from did not answer in time. Retry. |
upstream_failed | 502 | A third-party source answered with something we could not use. Retry; if it persists, the source is down and so are we for that field. |
mail_failed | 502 | The key was minted but the delivery email did not send. Your key exists — request it again rather than minting a second one. |
One code arrives on a 200. It is a statement about configuration, not a failure.
| Reason | Status | What it means & what to do |
|---|---|---|
x_not_configured | 200 | The X/Twitter integration is not configured on this deployment. The surface answers honestly rather than pretending to have no mentions. |
The one distinction worth coding carefully:
entitlement_unavailable is a 503, not a 402. It means we could not check your
subscription, not that you lack one. Treating it as a downgrade will lock a paying customer out of
a product they are paid up on, during an outage that is ours.
Codes are stable. If one has to change we will add the new one alongside the old and keep both answering. Related: how we handle missing data · live status · openapi.json · the MCP & API
Market data on this page is delayed and provided for general information only. It is not financial advice or a recommendation to trade. VIX/VXN/RVX are ~15-minute delayed (CBOE); index values use E-mini futures. Options trading involves significant risk of loss. © 2026 NoVo Options Trading LLC, d.b.a. NoVo Crypto Trading.