---
title: "Get Replay Range"
method: GET
path: "/api/replay/range"
tags: ["Replay"]
---

# Get Replay Range

`GET /api/replay/range`

## Request

- Base URL: `https://api.zerogex.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.zerogex.io/api/replay/range`
- Auth: HTTP bearer

All replay frames for one session — bundled for the playhead buffer.

Returns per-minute ``gex_summary`` + ``gex_by_strike`` bars for the
requested ET session date so a single round-trip pre-warms an entire
session into the browser. The scrubber then renders locally without
a per-frame fetch.

``strike_band_pct`` filters strikes to a ±band around each bar's
spot so the payload stays bounded — a full-chain SPX session would
be ~40k rows otherwise. Default 4% covers the strikes that actually
matter for a same-day dealer-positioning view.

``timeframe`` is accepted but currently ignored: we always return
1-min frames. 5-min / 15-min down-sampling is a v2 optimization
when payload size becomes a real problem.

``include_expirations`` opts into the per-strike expiration mix that
drives the scrubber's expiry colour gradient. Each strike then also
carries ``call_shares`` / ``put_shares`` — fractions summing to 1,
aligned positionally to the response's top-level ``expirations`` legend
(nearest-first, with a trailing "far" slot when the chain runs deeper
than ``max_expirations``). Shares only ever SUBDIVIDE the call/put totals
already in the payload, so a client that ignores them renders exactly what
it renders today. Off by default because it costs a second session-wide
scan and grows the bundle; callers that only need the aggregate ladder
(e.g. the pair-comparison scrubber) should leave it off.

``expirations`` scopes the session.  ``0dte`` — the case the replay exists
for — is the session's own expiry: on ``/replay/SPY/2026-09-15`` that is
2026-09-15, resolved from the requested date rather than from today, so a
shared link keeps meaning what it meant.  Under a scope every per-strike
bar carries only those expirations, and ``call_wall`` / ``put_wall`` /
``gamma_flip`` / ``max_pain`` are re-derived from that same filtered book
(the stored ones are whole-chain and would describe bars that aren't on
screen).  ``pin_strike`` / ``pin_confidence`` and ``max_gamma_strike`` do
NOT move with the scope — both are whole-chain by definition, exactly as
they behave under ``/api/gex/strike-profile-timeseries``'s filter and in
the live charts' Expiry selector.  The response echoes the resolved scope
as ``expiration_filter`` (``null`` for All) so a client never has to infer
what it got.

A scoped session with no matching contracts (0DTE on a day the chain had
no same-day expiry) returns its frames with EMPTY ladders and null levels
rather than silently falling back to the whole chain — "all expirations" is
the one answer a 0DTE request must not quietly become.

## Query parameters

- `symbol` string
- `date` string, required: Trading day YYYY-MM-DD (ET).
- `timeframe` string
- `strike_band_pct` number
- `include_expirations` boolean: Attach the per-strike expiration mix (shares) so a client can colour-grade each bar by time-to-expiry. Off by default: it is a second session-wide scan and it grows the payload.
- `max_expirations` integer
- `expirations` string: Expiration scope for the whole session: 'all' (default, every expiration aggregated), '0dte' (the session's own expiry), or a comma-separated list of YYYY-MM-DD dates. Under a scope the ladder carries only those expirations and the walls / flip / max pain are re-derived from it.

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zerogex/apis/zerogex-api.md) · [All operations](https://skmtc.dev/zerogex/apis/zerogex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zerogex/apis/zerogex-api/revisions/753b26bf0592?raw)
