Docs
GET /v2/chain/{symbol}/expirations

Listed option expiries for one underlying

chain_getExpirations

The distinct expiration dates of the symbol's listed option chain, ascending. Use it to pick an expiries= filter for GET /v2/gex/{symbol}/grid or an expiry= for GET /v2/chain/{symbol}.

Path parameters

symbol string required

Underlying symbol, e.g. SPY, SPX, AAPL

Query parameters

at string epoch ms

Point in time to read instead of the live capture: epoch-ms, or a YYYY-MM-DD / YYYY-MM-DDTHH:MM[:SS] string interpreted in tz.

tz string

IANA timezone for a string at (default America/New_York).

expiry string ISO date

Keep one expiry, ISO YYYY-MM-DD.

strike_gte string 1/1000 $

Strike lower bound, integer thousandths (inclusive).

strike_lte string 1/1000 $

Strike upper bound, integer thousandths (inclusive).

Details

Only at and tz change this answer; the chain's expiry and strike filters are accepted but do not narrow the list.

Units

Dates are ISO YYYY-MM-DD; meta.captured_at is epoch milliseconds.

Freshness

The live list comes from the latest chain capture and is cached for up to a minute; a symbol without regular captures is listed once per day. meta.captured_at says when the listed capture was taken, and meta.cached is true when the answer came from the cache. Expiries that have already settled are never served live: after the 16:00 ET close the day's expiry is dropped. With at=, the stored capture for that time is returned as it was.

Limits

No per-operation limit applies to requests with an API key.

Empty results

A symbol with no listed options, or an unknown symbol, returns 200 with an empty data list and meta.count = 0.

Example

http
GET /v2/chain/SPY/expirations
json
{
  "data": ["2026-09-28", "2026-09-29"],
  "meta": {
    "count": 19,
    "captured_at": 1790367005429,
    "underlying": "SPY"
  }
}

Response

Prices are dollars, timestamps epoch milliseconds, strikes integer thousandths of a dollar; null means unknown. Units →

200application/json

ISO YYYY-MM-DD expiry dates, ascending, in data; meta.count, meta.captured_at, meta.cache

object

A successful response: the payload consumers parse plus response metadata. data shapes are the platform types — never reshaped by the envelope.

data array<string> required
meta object required

Response metadata.

caps object nullable

meta.caps — the entitlement caps that shaped this response. applied lists the caps that actually changed it (for example range_clamped).

applied array<string> required

Which caps actually shaped this response (e.g. range_clamped). Empty = nothing was clamped.

darkpool boolean nullable

Whether this account can use Off-Exchange in the site.

flow boolean nullable

Whether the OPRA option-flow capability (flow) is granted to this principal in the app: site Pro plus a CBOE non-professional attestation (never on an itm_ key). Emitted explicitly — true and false — by the account endpoints, so the app reads the grant instead of inferring it from tier and attestation; absent on routes that do not report it.

gexDepth string nullable

GEX data depth when the route reports it. Camel-cased to match the browser capability bundle; absent on unrelated routes.

tier string required

The tier the request was served under.

cursor string nullable

Opaque continuation cursor (pass back as ?cursor=); absent when there is nothing more to read.

plane string nullable

Which serving tier answered the read (meta.plane). Informational provenance: treat it as an opaque label, because new values may appear.

Errors

Every error is the JSON error envelope; branch on error.code.

StatusMeaning
400

invalid_params — bad symbol or at

401

unauthenticated / token_expired

502

internal — the selected source failed upstream

503

internal — the selected source is not configured here