# GET /v2/series

**Get a series**

One call, up to 50 tickers × 25 columns, one shared time grid. `?tickers=AAPL,MSFT&columns=close,rsi_14` returns each ticker's series over the SAME timestamps — the join across the OHLCV bar store and the indicator/flag 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 `_meta` reports 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` (or `o`/`h`/`l`/`c`/`v`/`vw`/`n`) read licensed OHLCV bars; every column on the [schema page](/docs/schema) reads the state tables; your own [custom signals](/docs/endpoints/signals/using) compile per-row and behave exactly like built-ins. With no `columns` the default set matches [Ticker history](/docs/endpoints/tickers/history-series) — a bare `/v2/series?ticker=AAPL` IS ticker history in its canonical spelling.

Intervals: `1m` / `1h` / `1d` are stored tiers. `1w` is a real weekly resample — each state column samples the week's last observation, bars aggregate (open of first bar, max high, min low, close of last, summed volume), rows keyed by the week's Monday. `1q` serves 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 exact `fiscal_period`, and quarterly columns can't mix with daily ones in one request (the grids aren't joinable).

`transitions_only=true` filters rows down to the state-change edges of the boolean columns — computed by the same shared rule that fires webhooks, so the two can't disagree — with non-boolean columns riding along as context (the price at the flip). Numeric-threshold flips ("when did rsi_14 cross 70") are an [as-of replay](/docs/asof) question, not a transitions one.

For charting, [`/v2/tickers/{t}/bars`](/docs/endpoints/tickers/bars) keeps its own route 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 feed.

## Plan access

- **Plan access.** Included on every plan.
- **Rate limit.** Free 60/min · Hobby 600/min · Pro 6,000/min · Scale 60,000/min.
- **History.** Series history is all-time on every plan. Point-in-time `asof` reaches back 30 days on Free, unlimited on every paid plan.

## Query / path parameters

| Name | In | Type | Required | Description |
|------|----|----|----------|-------------|
| `tickers` | query | string | yes | Comma-separated symbols, up to 50 (POST accepts a JSON array). Required unless `ticker` (singular) is passed instead — and when both are passed, `ticker` wins, same precedence as `/v2/events`. Example: `AAPL,MSFT`. |
| `columns` | query | string | no | Comma list of up to 25 columns (POST accepts an array): OHLCV names, schema columns, and your custom signals, freely mixed. Omitted → the ticker-history default set (price, day_change_pct, relative_volume, market_cap), intersected with what the interval carries. At `1q`, `columns` is required and quarterly-only. Example: `close,rsi_14,above_sma_200`. |
| `interval` | query | string | no | Grid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid. Enum: `1m`, `1h`, `1d`, `1w`, `1q`. Default: `1d`. |
| `from` | query | string | no | Earliest 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. |
| `to` | query | string | no | Latest timestamp (inclusive), `YYYY-MM-DD` or ISO. |
| `limit` | query | integer | no | Grid steps per page (shared across tickers). Max 1000, and tickers × limit may not exceed 25,000 rows per page — over the cap is an explicit 400, never a silent clamp. Default: `252`. |
| `cursor` | query | string | no | Opaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates. |
| `transitions_only` | query | boolean | no | Only rows where a boolean column changed state. Requires at least one boolean column (built-in flag or custom signal); each returned row carries `transitions: {column: "enter"\|"exit"}`, and `_meta` lists the driving columns. |

## Status codes

- **200** — Envelope with `interval`, `tickers`, `columns`, `count`, `next_cursor`, `_meta` (`sources` per column, `coverage` per ticker, plus `non_trading_days_dropped` / `transitions_only` / `from_defaulted` when applicable), and `series` — per-ticker arrays of flat rows, chronological ASC.
- **400** — Bad interval/bounds, unknown column (`invalid_query`), daily-only column at an intraday interval, quarterly/daily column mix, over the tickers × limit cap, or `transitions_only` without a boolean column.
- **401** — Missing or invalid API key.

## Sample response

```json
{
  "as_of": "2026-07-29T20:10:12.000Z",
  "interval": "1d",
  "tickers": ["AAPL", "MSFT"],
  "columns": ["close", "rsi_14"],
  "count": 6,
  "next_cursor": "eyJiZWZvcmVfdHMiOiIyMDI2LTA3LTIzIn0",
  "_meta": {
    "sources": { "close": "bars", "rsi_14": "state" },
    "coverage": { "AAPL": { "rows": 3, "missing_bar_cells": 0 }, "MSFT": { "rows": 3, "missing_bar_cells": 0 } }
  },
  "series": {
    "AAPL": [
      { "t": "2026-07-27", "close": 336.51, "rsi_14": 65.23 },
      { "t": "2026-07-28", "close": 337.90, "rsi_14": 67.29 },
      { "t": "2026-07-29", "close": 339.97, "rsi_14": 68.88 }
    ],
    "MSFT": [
      { "t": "2026-07-27", "close": 509.42, "rsi_14": 58.11 },
      { "t": "2026-07-28", "close": 511.06, "rsi_14": 59.02 },
      { "t": "2026-07-29", "close": 512.10, "rsi_14": 60.47 }
    ]
  }
}
```

## Examples

### Two tickers, price + RSI, one aligned daily grid

Request:

```shell
curl "https://api.tickerbot.io/v2/series?tickers=AAPL,MSFT&columns=close,rsi_14&limit=3" \
  -H "Authorization: Bearer YOUR_KEY"
```

Response (`200`):

```json
{
  "as_of": "2026-07-29T20:10:12.000Z",
  "interval": "1d",
  "tickers": ["AAPL", "MSFT"],
  "columns": ["close", "rsi_14"],
  "count": 6,
  "next_cursor": "eyJiZWZvcmVfdHMiOiIyMDI2LTA3LTIzIn0",
  "_meta": {
    "sources": { "close": "bars", "rsi_14": "state" },
    "coverage": { "AAPL": { "rows": 3, "missing_bar_cells": 0 }, "MSFT": { "rows": 3, "missing_bar_cells": 0 } }
  },
  "series": {
    "AAPL": [
      { "t": "2026-07-27", "close": 336.51, "rsi_14": 65.23 },
      { "t": "2026-07-28", "close": 337.90, "rsi_14": 67.29 },
      { "t": "2026-07-29", "close": 339.97, "rsi_14": 68.88 }
    ],
    "MSFT": [
      { "t": "2026-07-27", "close": 509.42, "rsi_14": 58.11 },
      { "t": "2026-07-28", "close": 511.06, "rsi_14": 59.02 },
      { "t": "2026-07-29", "close": 512.10, "rsi_14": 60.47 }
    ]
  }
}
```

### Flips only: when did AAPL cross its 200-day, with price at the flip

Request:

```shell
curl "https://api.tickerbot.io/v2/series?ticker=AAPL&columns=above_sma_200,close&transitions_only=true&from=2026-03-01" \
  -H "Authorization: Bearer YOUR_KEY"
```

Response (`200`):

```json
{
  "as_of": "2026-07-29T20:10:12.000Z",
  "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" } }
    ]
  }
}
```

### Quarterly fundamentals on the fiscal grid

Request:

```shell
curl "https://api.tickerbot.io/v2/series?ticker=AAPL&columns=eps,revenue&interval=1q&limit=2" \
  -H "Authorization: Bearer YOUR_KEY"
```

Response (`200`):

```json
{
  "as_of": "2026-07-29T20:10:12.000Z",
  "interval": "1q",
  "tickers": ["AAPL"],
  "columns": ["eps", "revenue"],
  "count": 2,
  "next_cursor": "eyJiZWZvcmVfdHMiOiIyMDI1LVE0In0",
  "_meta": {
    "sources": { "eps": "earnings", "revenue": "statements" },
    "coverage": { "AAPL": { "rows": 2 } }
  },
  "series": {
    "AAPL": [
      { "t": "2025-Q4", "fiscal_period": "2025-12-31", "eps": 2.84, "revenue": 143756000000 },
      { "t": "2026-Q1", "fiscal_period": "2026-03-31", "eps": 2.01, "revenue": 111184000000 }
    ]
  }
}
```

## Notes

- All-time on every plan — series history is never plan-gated.
- `ticker` (singular) and `tickers` are both accepted; when both are present, `ticker` wins.
- Caps: 50 tickers, 25 columns, `limit` ≤ 1000, tickers × limit ≤ 25,000 rows per page. Every cap violation is an explicit 400 — nothing is silently clamped.
- 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.
- [Ticker history](/docs/endpoints/tickers/history-series) and [Signal history](/docs/endpoints/signals/history) return the same data for one ticker / one column in their original envelopes — kept stable for existing integrations. New integrations should read series.

---

Interactive sandbox + parameter editor: https://tickerbot.io/docs/endpoints/series/get
