Skip to main content
Rules that hold across every /v1 endpoint. The three that most often surprise people are decimal strings, staleness, and clamping.

The envelope

Every success is { data, meta }. Failures replace both keys with a single error object. There is no partial response: you get data and meta, or you get error.

Levels are decimal strings

indexLevel and baseIndexLevel are JSON strings, not numbers.
They are exact as published. A float round-trip silently changes the last digits, and a level that fails to reconcile against a published figure by one ulp is worse than useless in a benchmark. Parse them with a decimal type.
Every index is based at 100 on its inception date, which is what lets a level read on its own without a reference point.

Timestamps

Every timestamp on every endpoint is UTC, Z-suffixed, with microsecond precision.

Field naming

data and meta are camelCase, with one exception: the per-leg components array on the volatility endpoints is snake_case – leg_key, venue, condition_id, fbv_r_pts, passes_depth, observed_at. Those are the column names the snapshot bundles use for the same per-leg record, so the trailing window and full history join without two vocabularies. The one exception is venue, which the API carries and the bundle does not: published CSV headers are fixed at first publication and never gain a column, and the volatility leg files predate it. Every series published so far trades on a single venue, so a leg file is unambiguous without it.

Staleness is labeled, not hidden

stale is true when a level rests on inputs older than the freshness threshold. The level still publishes, and dataQuality on the latest endpoint reports how many constituents were fresh:
A stale reading is disclosed rather than suppressed, so decide what to do with it rather than assuming every value you receive is fresh.

Corrections carry a revision

Every index level on latest and in history carries revision: 0 for a level exactly as first published, 1 or higher once it has been corrected. The API serves the latest revision of each point. A corrected level is never changed silently, and the level as first published stays in the snapshot downloads. When and how a level is corrected is set out in Index Governance.

History windows clamp, they do not fail

History reaches back 90 days. A request for a longer window is clamped, not rejected: a chart that asked for a year still renders, and meta reports what it actually got.
Read clamped before labeling a chart with the range you asked for. A window falling entirely before the floor returns an empty points array with clamped: true rather than an error. An end in the future is clamped to now as well. What is a real error is a window that is empty once clamped – a start at or after end – because that is a mistake in your input rather than a limit of the free tier. It returns invalid_window, quoting both values as parsed, and names the future-end case outright when that is what emptied the window. Full history since inception is in the free snapshot bundles.

Ordering

There is no cursor pagination. History is bounded by the window; limit bounds the list endpoints that take one.

Tickers

Tickers are case-insensitive: conflict and CONFLICT resolve to the same index. Responses always echo the canonical uppercase form. An unknown ticker, an unlisted series, and a series with no published values yet all return the same 404. A ticker that is not on the API is not distinguishable from one that does not exist.

Request ids

X-Request-Id is on every response, including the two pre-authentication rejections. On an error it is also in the body as error.requestId. Quote it in any support message. It is what turns “I got a 500 around 14:02” into a single log lookup.

Versioning

/v1 is additive. New fields may appear in data or meta; existing fields will not change type or disappear without a new version. Parse defensively: ignore fields you do not recognize rather than rejecting the response. Data boundaries states what each lane carries and how /v1 changes over time.