Sign up

Products

Status

Tape & Intraday History

FIRM's OWN self-collected tick and intraday capture: full-market minute bars, per-print options and futures trade tape, futures term structure, live microstructure windows, and a daily options-chain history with greeks, implied vol and open interest. We have been snapshotting the tape into the lakehouse every trading day since May 2026. No competitor sells this history self-serve: options and futures vendors rent you today's prints and today's chain, but FIRM lets an agent replay the intraday tape and the greeks/IV/OI surface exactly as they stood on any past captured day.

What's inside

This product serves seven read surfaces over FIRM's day-partitioned tick/minute archives, self-collected and processed in-house:

  • Equity minute bars: 1-minute OHLCV + VWAP for ~10.5k tickers, extended hours included, resampled to 5m/15m/1h server-side, one bar per minute. A trade count is served only where the capture recorded one (see One bar per minute). Capture begins 2026-05-10.
  • Index minute bars: the same grain for the major index tapes SPX, NDX, DJI, RUT and VIX (index tapes carry no volume). Capture begins 2026-05-14.
  • Options trade tape: raw per-print options trades for one underlying, one day: premium (notional), strike, expiry, side, block and sweep flags, sorted largest-premium first. Capture begins 2026-05-11. Before 2026-07-30 only prints of $25,000 premium or more were kept; from then it covers names trading over $10M a day plus major ETFs and index options.
  • Futures trade tape: raw CME futures trade prints for one product root across every contract month: price, size, notional, block flag. Capture begins 2026-05-13. Before 2026-07-30 its coverage was concentrated in CME energy (NG-heavy, some CL), so an empty day then can mean the root was not captured; from 2026-07-30 it covers every outright of 28 CME, CBOT, NYMEX and COMEX products.
  • Futures curve: the daily futures-chain snapshot as a term structure: per contract month, last/settlement price, bid/ask, session OHLC, volume, open interest and days-to-expiry. Snapshots begin 2026-05-18 and are taken once a weekday at ~22:00 UTC.

Each archive knows when its capture last recorded rows. An empty day inside the archive carries that time as data_freshness. A day after it is refused with 503 (not charged), never served as an empty tape: archive_capture_stopped when the capture has missed its cadence, archive_not_yet_captured when it is running and has not reached that day (for example today's curve before the evening snapshot). Both name the archive's newest day. The options and futures trade tapes are served from three sources by UTC day, named on every answer as provenance: the original capture up to 2026-07-29, vendor trade history (rest_backfill) from 2026-07-30 to 2026-09-30, and the restarted live capture (live_capture) from 2026-10-01. A day that is still being backfilled answers 503 archive_gap_pending with Retry-After (not charged), never an empty tape.

Two live routes sit beside the futures archive, from the market-data feed rather than FIRM's capture:

  • Futures contracts: the tradeable outright contracts for one product root, front month first, with the true delivery month. One-digit year codes repeat every decade (ESH0 is the March 2030 contract here, never March 2020's), so the delivery month is pinned from each contract's own last trade date.
  • Live futures: one contract's (or a root's front month's) snapshot, recent trades and quotes, and daily session bars.
  • Intraday microstructure: 60-second windows for the most active tickers: rolling VWAP, trade count, average trade size, block-trade count/volume, buy/sell imbalance and large-trade premium. Coverage is the ~200 most active tickers per session (dynamic). Capture begins 2026-05-13.
  • Options-chain history: the ENTIRE captured chain (strike × expiry × side) for one underlying on any past snapshot day, with model greeks, implied vol, open interest and a chain summary. ~0.5 TB and growing; capture begins 2026-05-10.

The tick tables are huge and day-partitioned, so a date bound is mandatory on the trade-tape surfaces (options/futures prints) and a start..end window on the bar surfaces spans at most 5 trading days per call. Every response carries an honest note with the archive's capture-start date so you never retry-loop on a pre-archive day, and reader faults degrade to an explicit error/warning field rather than a 500.

Access

Note:

3 credits/call · Pro and up. The Tape & Intraday History product is not available on Free or Starter. It unlocks on Pro ($499/mo) and Expert/Enterprise. This is FIRM's own self-collected capture; there is no vendor add-on equivalent.

Endpoints

MethodPathMCP toolDescription
GET/api/v3/tape/bars/{ticker}tape_barsEquity minute bars (1m native; 5m/15m/1h resampled). Pass ?date=YYYY-MM-DD OR ?start=&end= (max 5 trading days), ?interval= (1m/5m/15m/1h, default 1m), ?limit= (1–10000, default 5000). Returns {ticker, window, interval, source, note, n, rows}; rows carry bar_start, open, high, low, close, volume, vwap, n_transactions. One row per minute, and n_transactions is null on most bars; see One bar per minute.
GET/api/v3/tape/index_bars/{index}tape_index_barsIndex minute bars for SPX/NDX/DJI/RUT/VIX (404 on any other index). Same date/start/end/interval/limit params. Returns {index, polygon_ticker, window, interval, source, note, n, rows}; rows carry bar_start, open, high, low, close, volume, vwap, n_ticks (index volume is zero).
GET/api/v3/tape/options/{ticker}tape_optionsRaw options trade prints for one underlying on one day. ?date= is required; ?min_premium= (notional_usd ≥), ?side= (C/P), ?limit= (1–5000, default 1000), ?cursor= (the previous page's next_cursor). Rows sorted largest-premium first. Returns {ticker, date, side, min_premium, source, note, n, rows_total, truncated, window, next_cursor, rows}; see Page a day that holds more prints than limit. rows carry option_ticker, contract_type, strike_price, expiration_date, trade_ts, price, size, notional_usd, exchange, is_block, is_sweep. A day after the capture's last write is a 503 (see above). For today's options use intel_options_chain and intel_options_contract.
GET/api/v3/tape/futures/{root}tape_futuresRaw CME futures trade prints for one product root (e.g. NG, CL, ES, GC, ZN) on one day. ?date= is required; ?limit= (1–5000, default 1000), ?cursor= (the previous page's next_cursor), ?before= (an ISO-8601 instant: start at the prints traded before it). Rows are the day's most recent prints (ascending by time). Returns {root, date, source, known_roots, note, n, rows_total, truncated, window, next_cursor, rows}; see Page a day that holds more prints than limit. rows carry contract_ticker, product_code, trade_ts, price, size, notional_usd, exchange, is_block. A day after the capture's last write is a 503 (see above). For today's prints use tape_futures_live.
GET/api/v3/tape/futures_curve/{root}tape_futures_curveFutures term structure (chain snapshot) for one root, 27 roots total. Omit ?date= for the latest snapshot, or pass ?date=YYYY-MM-DD; ?limit= (1–500, default 120). Returns {root, snapshot, source, known_roots, note, n, rows_total, truncated, snapshot_ts, rows} (truncated is true when the snapshot holds more contracts than limit); rows carry contract_ticker, contract_month, expiration_date, dte, last_price, bid, ask, midpoint, settlement_price, previous_settlement, change_percent, session_volume, open_interest. A date after the newest snapshot is a 503 archive_not_yet_captured whose hint names the newest day.
GET/api/v3/tape/futures_contracts/{root}tape_futures_contractsLive list of the tradeable outright contracts for one root (ES, NQ, CL, GC, ZN, 6E ...), front month first. ?limit= (1–200, default 24). Returns {root, reference_date, front_month, n, total_listed, rows, source, mode, fetched_at, note}; rows carry contract_ticker, contract_month, first_trade_date, last_trade_date, unambiguous_from, expiration_date, settlement_date, dte, trading_venue, is_front_month. unambiguous_from is the first session whose data can only be this contract's: a contract can be listed while the same-named contract of ten years earlier still trades (the 2029 CLV9 was listed on 2019-06-10; the 2019 one traded until 2019-09-20). front_month is the nearest last trade date.
GET/api/v3/tape/futures_live/{symbol}tape_futures_liveLive data for one contract (ESZ6) or a root's front month (ES). ?include= (comma list of snapshot, trades, quotes, daily), ?limit= (1–1000, default 50, applied to trades and to quotes), ?days= (1–370, default 30 days of daily bars). Returns {symbol, root, contract_ticker, contract, snapshot, trades, quotes, daily_bars, components, namesake_rows_dropped, as_of, fetched_at}. The snapshot's session carries session_date (the last session with trades, which for a thin contract can be weeks old); previous_settlement is the settlement of the session before it, dated by previous_settlement_date, and change is measured from it. Daily bars carry settled: false until the session settles. Rows dated before unambiguous_from are dropped and counted in namesake_rows_dropped, and a snapshot block that cannot be dated to this contract is null, with snapshot_withheld saying why. A dead feed is a 503.
GET/api/v3/tape/microstructure/{ticker}tape_microstructure60-second microstructure windows for one ticker (≈200 most active tickers/session). Omit ?date= for the most recent windows; ?limit= (1–5000, default 500). Returns {ticker, window, source, note, n, rows}; rows carry as_of_ts, vwap_60s, trade_count, total_volume, avg_trade_size, block_trade_count, block_trade_volume, trade_imbalance, large_trade_premium.
GET/api/v3/tape/options_chain/{ticker}tape_options_chainHistorical full options chain (per-contract greeks/IV/OI) for one underlying on one snapshot day. Omit ?date= for the LATEST captured day (reported as snapshot_day). Filters: ?contract_type= (call/put), ?expiration=, ?min_dte=/?max_dte= (0–3650), ?min_open_interest=, ?min_volume=, ?moneyness= (±band, 0–5.0), ?sort= (open_interest/notional, default open_interest), ?limit= (1–2000, default 100). Returns {ticker, mode, snapshot_day, snapshot_ts, source, note, summary, contract_type, sort, n, rows}.

Examples

Example 1: Equity minute bars around an event (bash + curl)

Pull 5-minute bars for AAPL for a single session (3 credits per call). Use date= for one day, or start=&end= for a window of up to 5 trading days.

Shell
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \  "https://firm.wealthnow.io/api/v3/tape/bars/AAPL?date=2026-07-16&interval=5m&limit=200" # Response envelope:# {#   "ok": true,#   "timestamp": "2026-07-17T20:15:00Z",#   "ticker": "AAPL",#   "window": "2026-07-16",#   "interval": "5m",#   "source": "warehouse:tape-archive",#   "note": "Own minute-bar capture begins 2026-05-10; earlier dates have no rows.",#   "n": 78,#   "rows": [#     {#       "bar_start": "2026-07-16T13:30:00Z",#       "open": 210.44,#       "high": 211.02,#       "low": 210.10,#       "close": 210.88,#       "volume": 1842300,#       "vwap": 210.61,#       "n_transactions": null#     }#     // ... more 5-minute bars#   ]# }

The same shape serves the index tapes: swap in /api/v3/tape/index_bars/SPX (or VIX) for the market/vol benchmark leg. Index rows carry n_ticks and a zero volume.

One bar per minute

The equity minute capture has two producers: the market-data websocket, which is the primary source, and a REST snapshot fallback. Since 2026-09-25 tape/bars serves one row per minute: the websocket's final bar for that minute when the websocket wrote it (after the close the websocket re-sends the last minute with the auction prints, and the later one is kept), and the fallback's bar only for a minute the websocket never wrote. It used to keep whichever row was written last, which was the fallback's on every minute both wrote.

The websocket's bars carry no trade count, so n_transactions is null on them; the number stored there before 2026-09-24 was the average trade size. A 5m, 15m or 1h bar's n_transactions is null unless every minute in it has a count. Index bars (tape/index_bars) keep summing n_ticks. The read also reaches further before it deduplicates, so a five-session window no longer loses its oldest bars to a row limit.

Example 2: The options and futures trade tape (bash + curl)

The trade-tape surfaces require a date= (the tables are day-partitioned and large). Options prints come back largest-premium first; min_premium narrows to the whales.

Shell
# Largest TSLA options prints on a day, notional >= $250k (3 credits)curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \  "https://firm.wealthnow.io/api/v3/tape/options/TSLA?date=2026-07-16&min_premium=250000&limit=50" # Response envelope:# {#   "ok": true,#   "timestamp": "2026-07-17T20:16:30Z",#   "ticker": "TSLA",#   "date": "2026-07-16",#   "side": null,#   "min_premium": 250000.0,#   "source": "warehouse:tape-archive",#   "note": "Own options-trade capture begins 2026-05-11. Rows are the largest-premium prints of the day (sorted server-side); raise min_premium to narrow the tape further.",#   "n": 41,#   "rows": [#     {#       "option_ticker": "O:TSLA260918C00300000",#       "contract_type": "call",#       "strike_price": 300.0,#       "expiration_date": "2026-09-18",#       "trade_ts": "2026-07-16T15:42:11Z",#       "price": 12.35,#       "size": 500,#       "notional_usd": 617500.0,#       "exchange": 312,#       "is_block": true,#       "is_sweep": false#     }#     // ... more prints, descending by notional_usd#   ]# } # CME futures trade tape: one product root, one day (3 credits).# The trade archive's coverage was energy-heavy (NG, CL); other roots may be empty.curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \  "https://firm.wealthnow.io/api/v3/tape/futures/NG?date=2026-07-16&limit=100" # Response envelope:# {#   "ok": true,#   "timestamp": "2026-07-17T20:17:05Z",#   "root": "NG",#   "date": "2026-07-16",#   "source": "warehouse:tape-archive",#   "known_roots": ["6B","6E","6J","CL","ES","GC","HE","HG","HO","LE","NQ","PA","PL","RB","RTY","SI","UB","YM","ZB","ZC","ZF","ZL","ZM","ZN","ZS","ZT","ZW"],#   "note": "Own futures-trade capture begins 2026-05-13; its coverage was concentrated in CME energy (NG, CL), so other roots can be empty even on captured days. ...",#   "n": 100,#   "rows": [#     {#       "contract_ticker": "NGQ6",#       "product_code": "NG",#       "trade_ts": "2026-07-16T19:58:44Z",#       "price": 3.412,#       "size": 4,#       "notional_usd": 136480.0,#       "exchange": 42,#       "is_block": false#     }#     // ... most recent prints of the day, ascending by trade_ts#   ]# }

Page a day that holds more prints than limit

One call returns at most 5,000 prints, and a busy day holds many times that. An answer from tape/futures or tape/options says when it left prints out, how many the day holds, and how to read the rest:

FieldMeaning
rows_totalThe day's exact print count for the request: the root and date on futures; the ticker, date, side and min_premium on options. null only when the count could not be read, and rows_total_reason then says why (fallback_scan_capped or count_unavailable).
truncatedtrue when the day holds more prints than this answer returned.
windowWhich prints the first page holds: most_recent on futures (the day's newest prints, served oldest first), largest_premium on options (largest notional_usd first).
next_cursorAn opaque string. Pass it back as cursor for the next page: the next-older prints on futures, the next-largest on options. null on the last page.

To read a whole day, repeat the call with cursor set to the last answer's next_cursor until it is null. Pages never repeat or skip a print. Prints that are identical in every field (a sweep can print several times in one millisecond) are each served once, so the pages of a finished day add up to exactly rows_total prints. A day that is still being written can gain prints between pages. Each page is a call and costs 3 credits.

A cursor belongs to the request that issued it: the same root or ticker, the same date and, on options, the same side and min_premium. limit can change between pages.

On tape/futures, before=<ISO-8601 instant> (no offset means UTC) starts at the most recent prints traded strictly before that time instead of at the end of the day. It is coarse: prints that share one trade_ts all fall on one side of it. Continue from that page with cursor.

A bad position is a 422, refused before anything is read and not billed:

errorSent when
invalid_cursorcursor is not one this route issued, or it was issued for another root, ticker, date or filter.
invalid_beforebefore is not an ISO-8601 instant such as 2026-07-16T15:30:00Z.
cursor_and_beforeBoth were sent. cursor continues a paging sequence; before starts one.

An empty day answers rows_total: 0, truncated: false and next_cursor: null, with no window.

The first page of a day larger than limit (abridged; the values are illustrative):

JSON
{  "ok": true,  "root": "NG",  "date": "2026-07-16",  "n": 5000,  "rows_total": 18342,  "truncated": true,  "window": "most_recent",  "next_cursor": "eyJ2IjoyLCJzIjpbInRhcGUvZnV0dXJlcyIsIk5HIiwiMjAyNi0wNy0xNiJd...",  "rows": []}

Read the whole day, oldest print first:

Python
import os, requests H = {"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"}URL = "https://firm.wealthnow.io/api/v3/tape/futures/NG" params = {"date": "2026-07-16", "limit": 5000}pages = []while True:    r = requests.get(URL, headers=H, params=params, timeout=60)    r.raise_for_status()    page = r.json()    pages.append(page["rows"])            # ascending inside a page    if not page["next_cursor"]:           # null on the last page        break    params["cursor"] = page["next_cursor"]  # the next-older page, 3 credits # Pages arrive newest first, so reverse them for one ascending day.prints = [row for rows in reversed(pages) for row in rows]print(f"{len(prints)} prints read, rows_total {page['rows_total']}")

Example 3: Futures curve + microstructure (Python)

futures_curve returns one snapshot day as a term structure (omit date for the latest). microstructure returns the 60-second execution-grade windows for the active-ticker set.

Python
import os, requests H = {"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"} # 1. Latest CL (crude) term structure: contango/backwardation + open interest (3 credits)r = requests.get("https://firm.wealthnow.io/api/v3/tape/futures_curve/CL",                 headers=H, timeout=30)r.raise_for_status()curve = r.json()print(f"CL curve snapshot {curve.get('snapshot_ts')}}, {curve['n']} contracts")for c in curve["rows"][:6]:    print(f"  {c['contract_ticker']}  dte={c['dte']:>4}  "          f"settle={c.get('settlement_price')}  OI={c.get('open_interest')}") # 2. Microstructure: is smart money accumulating? (positive imbalance + block activity)r = requests.get("https://firm.wealthnow.io/api/v3/tape/microstructure/MSFT",                 headers=H, params={"limit": 30}, timeout=30)r.raise_for_status()micro = r.json()# micro is empty if MSFT wasn't in that session's ~200 most-active setfor w in micro["rows"][-5:]:    print(f"  {w['as_of_ts']}  vwap={w['vwap_60s']}  "          f"imbalance={w['trade_imbalance']}  blocks={w['block_trade_count']}")

Example 4: Historical options-chain snapshot with greeks (Python)

Reconstruct the IV surface / greeks / OI distribution AS IT STOOD on a past day. Omit date for the latest captured snapshot (the served day is echoed back as snapshot_day); the summary block is always over the full captured chain, before your row filters.

Python
import os, requests H = {"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"} # Near-the-money AAPL chain on a snapshot day, both sides, >=100 OI (3 credits)r = requests.get(    "https://firm.wealthnow.io/api/v3/tape/options_chain/AAPL",    headers=H,    params={"date": "2026-07-16", "moneyness": 0.1,            "min_open_interest": 100, "sort": "open_interest", "limit": 50},    timeout=30,)r.raise_for_status()chain = r.json()# Response envelope (abridged):# {#   "ok": true,#   "timestamp": "2026-07-17T20:19:00Z",#   "ticker": "AAPL",#   "requested_date": "2026-07-16",#   "mode": "snapshot",              # "latest_available" when date is omitted#   "snapshot_day": "2026-07-16",#   "snapshot_ts": "2026-07-16T22:01:33Z",#   "source": "warehouse:tape-archive",#   "note": "FIRM's own daily options-chain capture begins 2026-05-10; ... Per-day coverage is the top open-interest contracts per underlying (a few hundred), not the full listed chain; raise min_open_interest / min_volume to focus, or widen moneyness to see more strikes.",#   "summary": {#     "underlying_price": 210.55,#     "n_contracts": 480,#     "n_calls": 242,#     "n_puts": 238,#     "n_expirations": 14,#     "call_open_interest": 1284500.0,#     "put_open_interest": 1102300.0,#     "put_call_oi_ratio": 0.8582,#     "call_volume": 318400.0,#     "put_volume": 271900.0,#     "put_call_volume_ratio": 0.8539,#     "total_open_interest": 2386800.0,#     "total_day_volume": 590300.0,#     "atm_iv": 0.296,#     "atm_strike": 210.0,#     "atm_expiry_dte": 32.0#   },#   "contract_type": null,#   "sort": "open_interest",#   "n": 50,#   "rows": [#     {#       "option_ticker": "O:AAPL260821C00210000",#       "contract_type": "call",#       "strike_price": 210.0,#       "expiration_date": "2026-08-21",#       "dte": 36,#       "bid": 6.85, "ask": 6.95, "midpoint": 6.90, "last_price": 6.88,#       "day_volume": 12840, "day_vwap": 6.91,#       "implied_volatility": 0.298,#       "delta": 0.531, "gamma": 0.021, "theta": -0.089, "vega": 0.164,#       "open_interest": 48210,#       "underlying_price": 210.55#     }#     // ... top contracts by open interest, after the moneyness/OI filters#   ]# }print(f"{chain['ticker']} {chain['snapshot_day']}: "      f"P/C OI {chain['summary']['put_call_oi_ratio']}, "      f"front-month ATM IV {chain['summary']['atm_iv']}")

Coverage & caveats

  • Capture start dates differ per stream (all surfaced in each response note): minute bars 2026-05-10, options trades 2026-05-11, futures trades and microstructure 2026-05-13, index bars 2026-05-14, futures-chain snapshots 2026-05-18, options-chain history 2026-05-10. Dates before a stream's start return an empty rows with a no_rows warning, not an error. On the options tape, futures tape and futures curve, a date after the capture's last write is a 503 refusal naming the newest captured day, not an empty result.
  • The futures trade archive is energy-heavy. Its per-print coverage was concentrated in CME energy (NG-heavy, some CL). An empty captured day for another root means that root was not captured, not that the market was quiet. Use /tape/futures_curve for the 27-product chain snapshots and /tape/futures_live for today's prints.
  • Microstructure is a dynamic ~200-ticker set. Only the most active tickers per session are captured; an empty result means the ticker wasn't active enough that session.
  • Options-chain history is top-of-book by open interest. Per-day coverage is the top open-interest contracts per underlying (a few hundred), not the full listed chain. Narrow with min_open_interest/min_volume or widen moneyness to change the view. With no date=, the route serves the latest captured snapshot day (never a today-relative window that could read empty on a holiday or capture gap) and echoes it as snapshot_day.
  • A date bound is mandatory on the trade-tape surfaces (/tape/options, /tape/futures); bar windows span at most 5 trading days per call. These are hard 422 guards that keep unbounded scans of the multi-hundred-GB tick tables out.
  • Every response follows the standard envelope {ok, timestamp, ...}. Reader faults degrade to an explicit error/warning field, never a 500.