/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
symbolstring requiredUnderlying symbol, e.g.
SPY,SPX,AAPL
Query parameters
atstring epoch msPoint in time to read instead of the live capture: epoch-ms, or a
YYYY-MM-DD/YYYY-MM-DDTHH:MM[:SS]string interpreted intz.tzstringIANA timezone for a string
at(defaultAmerica/New_York).expirystring ISO dateKeep one expiry, ISO
YYYY-MM-DD.strike_gtestring 1/1000 $Strike lower bound, integer thousandths (inclusive).
strike_ltestring 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
GET /v2/chain/SPY/expirations{
"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.
| Status | Meaning |
|---|---|
| 400 |
|
| 401 |
|
| 502 |
|
| 503 |
|