State over time
https://api.tickerbot.io/ v2/ seriesAny signals for any tickers on one shared time grid — up to 50 tickers by 25 columns per call.
Query parameters
string[]Comma-separated symbols, up to 50 (POST accepts a JSON array). Exactly one of tickers or ticker is required; when both are passed, ticker wins — so sending both silently narrows the request to one symbol.
stringSingle-symbol form — /v2/series?ticker=AAPL is ticker history in its canonical spelling. Exactly one of ticker or tickers is required.
string[]Up to 25 columns (POST accepts an array): OHLCV names, signals, and your custom signals, freely mixed. Omitted → the ticker-history default set (price, change_1d_pct, relative_volume, market_cap), intersected with what the interval carries. At 1q, columns is required and quarterly-only. fields accepted as an alias.
enumdefault 1dGrid granularity. 1w resamples the daily tier weekly (Monday-keyed); 1q is the fiscal-quarter grid.
1m | One minute. The finest stored tier; carries the intraday column subset and ~1,100 of ~13,700 tickers. |
1h | One hour. Stored tier, intraday column subset, full universe. |
1ddefault | One day. The full-history, full-column tier — the only grain that carries daily-only columns (SMAs, RSI, fundamentals). |
1w | One week, resampled: state columns take the week’s last observation, bars aggregate, rows keyed by the Monday. |
1q | One fiscal quarter. Serves quarterly fundamentals on the fiscal grid; cannot mix with daily columns in one request. |
stringEarliest timestamp (inclusive), YYYY-MM-DD or ISO. Intraday requests default to a recent window (1m: 7 days, 1h: 60 days) — the cursor keeps walking further back window-by-window, or pass from to widen it up front.
stringLatest timestamp (inclusive), YYYY-MM-DD or ISO.
stringPoint-in-time read: ONE row per ticker — the state at that instant — rather than a range. YYYY-MM-DD or a full ISO timestamp, the same meaning asof carries on /v2/tickers, /v2/scan and /v2/signals. Cannot be combined with from/to or cursor (400) — a point and a window are contradictory, and limit has no meaning under it. It also resolves WHICH COMPANY held the symbol at that instant: a ticker that changed hands returns the row of whoever traded it then, so tickers=SHLD&asof=2010-06-30 returns Sears Holdings' price and asof=2026-01-01 returns the Global X defence ETF. Returns the most recent row at or before the instant, so a date inside a trading gap gives the last row before it. At interval=1q the anchor is the date the quarter was REPORTED (earnings release / filing), not fiscal period end — you get the latest quarter that was public knowledge at the instant, with restatements after it excluded.
integerdefault 252Grid steps per page (shared across tickers). Max 1000 — an over-cap limit is clamped to 1000 (house convention, limit=10000 means "max"). Separately, tickers × limit may not exceed 25,000 rows per page — over THAT cap is an explicit 400.
stringOpaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates.
booleanOnly rows where a boolean signal changed state. Accepted spellings: true/1/yes and false/0/no (case-insensitive) — anything else is a 400, never silently off. Requires at least one boolean signal (built-in boolean or custom signal); each returned row carries transitions: {column: "enter"|"exit"}, and _meta lists the driving columns. Strict truth: only literal true is "on", so null → true is an enter and true → null an exit (a backfill boundary reads as an edge). Edges need a prior observation — on the oldest page of a walk the first row has no predecessor and yields no edge. A flip is dated by the state table and does not move with the column list: one recorded on a non-trading carry row keeps that date, with any bar columns null on that row (no bar exists there).
Returns
as_ofstringServer time this response was assembled (ISO 8601).
intervalstringThe grid granularity served.
tickersarraySymbols in the response, echoed.
columnsarrayColumns in the response, echoed.
countnumberRows per ticker in this page.
next_cursorstringOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.
_metaobjectPer-column `sources` (`bars`, `state`, or `custom` for your own signals; `earnings`/`statements` at 1q) and per-ticker `coverage`, plus `non_trading_days_dropped` / `transitions_only` / `from_defaulted` when they apply. `deprecated_columns` lists any column you named under its pre-2026-09-07 spelling that was served under its current name (`requested`, `use`, `note`).
seriesobjectKeyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for.
Status codes
200400invalid_query), daily-only signal at an intraday interval, quarterly/daily signal mix, over the tickers × limit cap, or transitions_only without a boolean signal.401Notes
- The join across the OHLCV bar store and the indicator and boolean state tables that would otherwise take N history calls and a hand-rolled merge. Those two stores genuinely disagree — state has rows on non-trading days, intraday bars can lag behind state — so the endpoint owns the alignment policy: a missing cell is
null, never a silently dropped row, and_metareports per-column sources plus per-ticker coverage so a gap is always visible. - Column names resolve by source, invisibly at the call site:
open/high/low/close/volume/vwap/trades(oro/h/l/c/v/vw/n) read licensed OHLCV bars; every signal on the schema page reads the state tables; your own custom signals compile per-row and behave exactly like built-ins. - With no
columnsthe default set matches the deprecated Ticker history — a bare/v2/series?ticker=AAPLIS ticker history in its canonical spelling. 1m/1h/1dare stored tiers.1wis a real weekly resample: each signal samples the week's last observation, bars aggregate (open of first bar, max high, min low, close of last, summed volume and trades, volume-weightedvwap—nullfor a zero-volume week), rows keyed by the week's Monday.1qserves quarterly fundamentals (eps,eps_estimate,eps_surprise,eps_surprise_pct,revenue,gross_profit,free_cash_flow) on the fiscal grid — rows keyed by calendar quarter (2026-Q2) with each ticker's exactfiscal_periodandreported, the date the quarter was announced (nullwhen unknown).reportedis the axis a point-in-time backtest filters on: the quarter's numbers were not knowable before it. Quarterly columns cannot mix with daily ones in one request — the grids are not joinable.transitions_only=truefilters rows down to the state-change edges of the boolean signals — computed by the same shared rule that fires webhooks, so the two cannot disagree — with non-boolean signals riding along as context (the price at the flip). Numeric-threshold flips ("when did rsi_14 cross 70") are an as-of question, not a transitions one.- For charting, Bars is its own family on purpose: sub-hour intervals (
1s–30m), fetch-on-miss coverage, and the compact array shape live there. Series is the analytical read; bars is the chart read. - Both
GETandPOSTare supported —POSTtakes the same parameters in a JSON body and accepts real arrays fortickers/columns. - All-time — series history is never windowed.
- At
1q, the two sources land at different times:epsarrives with the earnings announcement while statement signals (revenue,gross_profit,free_cash_flow) arrive with the filing. So the most recent quarter routinely carries anepswithrevenue: null— a normal reporting lag, not a coverage gap.reporteddates the announcement. ticker(singular) andtickersare both accepted; when both are present,tickerwins.- Caps: 50 tickers, 25 columns,
limit≤ 1000, tickers × limit ≤ 25,000 rows per page. Over-captickers,columns, and tickers × limit are explicit 400s; an over-caplimitalone is clamped to 1000 (house convention —limit=10000means "max"). - On a mixed bars+state daily request, spine rows on non-trading days (weekends/holidays) are dropped and counted in
_meta.non_trading_days_dropped— they could never carry OHLCV and would read as gaps.transitions_onlyrequests are exempt: their output is already sparse, so carry rows are never dropped and a flip recorded on one keeps its original date no matter which columns ride along (bar columns arenullon such a row and count in_meta.coverage.*.missing_bar_cells). Before 2026-08-10, adding a bar column re-dated such flips to the next trading day. - Ticker history and Signal history return the same data for one ticker / one signal in their original envelopes — kept stable for existing integrations. New integrations should read series.
More examples
curl "https://api.tickerbot.io/v2/series?ticker=AAPL&columns=above_sma_200,close&transitions_only=true&from=2026-02-18" \
-H "Authorization: Bearer YOUR_KEY"{
"as_of": "2026-08-11T23:23:33.317Z",
"interval": "1d",
"tickers": ["AAPL"],
"columns": ["above_sma_200", "close"],
"count": 2,
"next_cursor": null,
"_meta": {
"sources": { "above_sma_200": "state", "close": "bars" },
"coverage": { "AAPL": { "rows": 2, "missing_bar_cells": 0 } },
"transitions_only": true,
"transition_drivers": ["above_sma_200"]
},
"series": {
"AAPL": [
{ "t": "2026-03-30", "above_sma_200": false, "close": 246.63, "transitions": { "above_sma_200": "exit" } },
{ "t": "2026-03-31", "above_sma_200": true, "close": 253.79, "transitions": { "above_sma_200": "enter" } }
]
}
}curl "https://api.tickerbot.io/v2/series?ticker=AAPL&columns=eps,revenue&interval=1q&limit=2" \
-H "Authorization: Bearer YOUR_KEY"{
"as_of": "2026-08-11T23:23:33.317Z",
"interval": "1q",
"tickers": ["AAPL"],
"columns": ["eps", "revenue"],
"count": 2,
"next_cursor": "eyJiZWZvcmVfdHMiOiIyMDI2LVExIn0",
"_meta": {
"sources": { "eps": "earnings", "revenue": "statements" },
"coverage": { "AAPL": { "rows": 2 } }
},
"series": {
"AAPL": [
{ "t": "2026-Q1", "fiscal_period": "2026-03-31", "reported": "2026-04-30", "eps": 2.01, "revenue": 111184000000 },
{ "t": "2026-Q2", "fiscal_period": "2026-06-30", "reported": "2026-07-30", "eps": 1.91, "revenue": null }
]
}
}