Skip to main content
Rules that hold across every /v1 endpoint. The four that most often surprise people are decimal strings, the volatility timestamp format, 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

Timestamps are UTC with microsecond precision.
The volatility computation timestamps ship without a Z or offset. They are still UTC. A strict RFC 3339 parser will reject them, so attach the timezone yourself.

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.

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.