| Outcome | Top-level shape |
|---|---|
| Success | { "data": ... } |
| Failure | { "error": { "code", "message", "retryable" } } |
Success envelope
Success is exactly a top-leveldata object. Here is a real /v2/snapshot response for AAPL, trimmed to the wrapper and the common fields; the live response also carries quote, performance, range_52w, and fundamentals_quick blocks.
GET /v2/snapshot?symbol=AAPL
data is endpoint-specific. The wrapper is not.
Error envelope
Failure is exactly a top-levelerror object with three fields. This is a live 429 from /v2/snapshot:
GET /v2/snapshot?symbol=AAPL, HTTP 429
error.retryable is the retry contract. Retry only when it is true, and still back off: see error codes for the full table and the worked retry loop. On a 429 the response also carries rate-limit headers; this example sent Retry-After: 21 and X-RateLimit-Remaining: 0.
Common fields on data
Most symbol endpoints put these fields at the top of data. The exact set varies by endpoint, but when a field appears it means the same thing everywhere.
| Field | Meaning |
|---|---|
symbol | The resolved symbol for endpoints that take one (normalized, e.g. BRK-B). |
as_of | The timestamp the payload represents: upstream timestamp when the source gives one, otherwise generation time. UTC ISO-8601. See freshness. |
freshness | One of intraday, end_of_day, stale, degraded, unsupported. Owned by freshness. |
market_status | open or closed. Omitted on stateless lookup routes (/v2/search, /v2/coverage). Owned by freshness. |
cache_age_seconds | Seconds the served data has been cached. 0 means freshly computed; a large value means a cached read. Pair it with as_of to judge staleness. |
currency | Present only when the payload carries monetary values; USD across the supported universe. |
asset_type | stock or etf. Present where the two shapes branch. |
period | Present on period-based endpoints (/v2/fundamentals, /v2/history). Units owned by units and fields. |
cache_age_seconds is the field to read when you care whether a number is live. In the snapshot above it is 0 (computed on request); a /v2/fundamentals read for the same symbol returned 16096, meaning the statement model was served from a cache roughly four and a half hours old. That is expected for fundamentals, since statements do not change intraday, but it is the value to surface if your product needs to reason about how current a figure is.
Unsupported is a success, not an error
When an endpoint does not apply to a symbol, you get HTTP 200 with a normaldata object whose freshness is "unsupported" and whose reason is a machine-readable string. This is a live /v2/valuation for SPY, where ETFs have no valuation multiples:
GET /v2/valuation?symbol=SPY, HTTP 200
reason and move on. Coverage and gaps lists which families return unsupported for which assets.
Three ways a value can be absent
Inside adata object, a value can be missing in three distinct ways, and they mean different things. This is the table other pages link to; do not re-derive it elsewhere.
| Pattern | What it looks like | Meaning | What to do |
|---|---|---|---|
null | "field": null | The field is meaningful here, but no value is available for this row or symbol. | Render “not available” or omit the cell. Do not compute with it. |
| Omitted key | the key is simply not present | A persistent source gap, or an optional field that does not apply. | Treat as absent. Do not invent it. |
| Unavailable envelope | "field": { "available": false, "reason": "..." } | The value is intentionally suppressed, and reason says why. | Keep the reason if you display the field. Do not retry to “get the number.” |
{ "available": false, "reason": ... }. A real example from /v2/insider, where a gift has no market price:
interest_coverage becomes { "available": false, "reason": "zero_interest_expense" } rather than dividing by zero.
Parse by envelope, branching on retryable
A correct client readsdata versus error first, and on error keeps code and retryable so the caller can decide whether to retry. The two parsers below do exactly that.
- Python
- TypeScript
parse.py
NoxstockError and checks retryable before deciding to retry. The full backoff loop that honors Retry-After lives with the error codes.
Error codes
Every code, its HTTP status, whether it is retryable, and what to do about it.