History
Compute an indicator over a past range, with the same parameters as in real time. You get exactly what the real-time stream published at that moment.
History requires the Historical offer (or the Sandbox, limited to the last 7 days) and a key with the history scope.
Make a query
curl https://api.fathomcharts.com/v1/indicator-queries \
-H "Authorization: Bearer $FATHOM_CHARTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instrument":"NQZ6","indicator":"big-trades","params":{"minimum":30},"from":"1790256600000000000","to":"1790258100000000000"}'| Field | Required | Description |
|---|---|---|
instrument | yes | A specific contract (NQZ6) or the front month (NQ.front), as in real time. |
indicator | yes | The indicator id from the catalog. |
params | no | The indicator parameters, exactly as in real time. |
from, to | yes | Start (inclusive) and end (exclusive) of the range: a timestamp in nanoseconds since January 1, 1970 UTC, as a string. Here, 13:30 to 13:55 UTC on September 24, 2026. |
limit | no | Maximum number of results per page, from 1 to 10,000. Default: 1,000. |
cursor | no | To get the next page: the next value of the previous page. |
untilCursor | no | Stops the query at this cursor, inclusive. See Hand off to real time. |
dataset | no | Rarely needed: the data source (GLBX.MDP3) of a contract that is not streamed in real time. |
The query is rejected with 403 FORBIDDEN if your plan does not cover the requested range: no history included, a range older than your plan allows, or a range ending within the last 10 minutes while your data is delayed.
Read the response
{
"items": [
{"t":"upsert","cursor":"20720.111416.0","id":"1790256600533489285:0","final":false,"ts":"1790256600533489285","data":{"side":"sell","volume":31,"trades":15,"start":"1790256600533489285","end":"1790256600533489285","first":122041,"last":122027,"price":122027,"vwap":122034.25806451614}},
{"t":"upsert","cursor":"20720.111417.0","id":"1790256600533489285:0","final":false,"ts":"1790256600533489285","data":{"side":"sell","volume":34,"trades":16,"start":"1790256600533489285","end":"1790256600533489285","first":122041,"last":122026,"price":122026,"vwap":122033.5294117647}}
],
"next": null,
"computeUnits": 523875
}Above, the first two of the 323 items of this range (next is null: everything fits in one page).
items: the updates, in order, in the same shape as in real time;next: send it back to get the next page, ornullonce everything has been served;computeUnits: what this page cost against your compute budget.
History contains every version published live, including intermediate ones (final: false). Above, the cluster of sell trades shows up as soon as it crosses the 30-contract threshold, then is republished on every trade that extends it, up to its final version (66 contracts, final: true). If you only care about the finished result, keep final: true.
Objects that started before from are accounted for: a zone opened before the start of the range and updated during it appears exactly as it did live. To do so, the calculation also reads the trades before from that it needs, which is why a short range can cost more compute units than its own trades.
An empty list (items: [] and next: null) means the calculation ran and found nothing in the range, for example no big trade above the threshold. It is never a disguised error: if data is missing you get 402 NOT_COVERED; if the calculation is still warming up, 409 WARMING.
Next pages
As long as next is not null, send the same query again with "cursor": "<value of next>".
- A page can hold fewer than
limitresults, or none at all, before the range is done: onlynext: nullmarks the end. - If
nextis too old, the query is rejected withCURSOR_EXPIRED: start over from the beginning.
With the SDK, FathomChartsRest walks the pages for you. history() yields results one at a time, as you read them (and pages() yields one page at a time):
import { FathomChartsRest } from '@fathom-charts/sdk';
import type { BigTradesData } from './fathom-charts-types.js';
const rest = new FathomChartsRest({ baseUrl: 'https://api.fathomcharts.com', apiKey: process.env.FATHOM_CHARTS_API_KEY! });
const query = {
instrument: 'NQZ6',
indicator: 'big-trades',
params: { minimum: 30 },
from: '1790256600000000000',
to: '1790258100000000000',
};
for await (const m of rest.history<BigTradesData>(query)) {
if (m.t === 'upsert' && m.final && m.data) console.log(m.cursor, m.data.side, m.data.volume);
}On error, the SDK throws a FathomChartsError carrying the error code. When you hit the rate limit, it waits and retries automatically.
Estimate the cost
Every query is charged against your monthly compute budget. Before a heavy query, ask for an estimate: same request body, nothing is computed or charged.
curl https://api.fathomcharts.com/v1/indicator-queries/estimate \
-H "Authorization: Bearer $FATHOM_CHARTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instrument":"NQZ6","indicator":"big-trades","params":{"minimum":30},"from":"1790256600000000000","to":"1790258100000000000"}'{
"trades": 523875,
"computeUnits": 523875,
"coveredFrom": "1790121600000000000",
"coveredTo": "1790258100000000000",
"missingDays": [],
"remainingComputeUnits": 19999476125
}computeUnits: the cost of the query, that is the number of trades to read (trades) times the indicator cost (shown on its catalog page). The trades read include those beforefromthat the calculation needs;remainingComputeUnits: what is left of this month’s budget, before this query;coveredFrom,coveredTo: the range actually read, which starts beforefromwhen the calculation needs earlier trades (here from September 23, 00:00 UTC);missingDays: the days without data, which would make the query fail with402 NOT_COVERED.
With the SDK: await rest.estimate(query).
A query that would cost more than your remaining budget is rejected before it starts, with 429 QUOTA_EXCEEDED. The Retry-After header tells you when your budget renews.
Available range
Historical queries and exports cover the completed days (in UTC) published by our data provider: a range that touches the current day is rejected with 402 NOT_COVERED, and the message gives the last date served. To include the current day, subscribe on the WebSocket with from: {"time": …}: the stream chains history and live data with no gap (see Starting in the past).
Depth depends on your plan: 7 days on the Sandbox, the full available depth with the Historical offer.
Hand off to real time
To load the past and then follow the market with no gap and no duplicate, you have two options:
- the simplest: subscribe on the WebSocket with
from: {"time": "<start>"}. You receive the past, then live data, back to back. See Starting in the past; - in two steps: first open the real-time subscription and note the
snapshotcursor. Then run the history query withuntilCursorset to that cursor: it stops exactly where live data begins.