Docs/Market Structure API (GEX)
API referenceMarket Structure API (GEX)
GET /api/v1/market-structure/:symbol reference: structural Gamma Exposure, intraday GEX, Gamma Flip, walls, Max Pain, IV context, session flow, and symbol metadata.
On this page
Start here
Follow the Market Structure quickstart, jump to the request example, or review the response sections.
The Market Structure API returns a precomputed, symbol-level snapshot of full-chain dealer-positioning structure (GEX), open interest walls, Max Pain, volatility context, and five-minute session flow plus intraday GEX summaries.
GET https://www.optiondata.io/api/v1/market-structure/:symbolIncluded with the realtime plan (trialing or active). Authenticate with a signed API key.
Authentication
Send your API key as:
Authorization: Bearer YOUR_API_KEYYOUR_API_KEY may be a raw Stripe customer id (cus_...) or a portal-minted apikey_... token.
Path parameters
| Name | Required | Description |
|---|---|---|
symbol | Required | Underlying option root (e.g. SPY, SPXW, AAPL). Uppercased server-side. |
Query parameters
| Name | Required | Description |
|---|---|---|
date | Optional | Real YYYY-MM-DD calendar date selecting a retained trading-session snapshot. Omit for the latest available snapshot and check meta.effective_date. An explicit date is never replaced with another session. |
Example
curl "https://www.optiondata.io/api/v1/market-structure/SPY" \
-H "Authorization: Bearer YOUR_API_KEY"
curl "https://www.optiondata.io/api/v1/market-structure/SPY?date=YYYY-MM-DD" \
-H "Authorization: Bearer YOUR_API_KEY"Replace YYYY-MM-DD with a retained trading date. A weekend, holiday, future date, or session outside retention can return 404.
Response shape
Top-level:
| Field | Type | Description |
|---|---|---|
data.symbol | string | Exact option root requested |
data.symbol_meta | object | Underlying metadata and volatility context |
data.flow | object | Lightweight traded-flow overlay for the symbol |
data.intraday_gex | object | null | Latest full-chain summary GEX and spot |
data.structure | object | Full-chain GEX, OI, levels, strike-by-expiration aggregates |
meta.effective_date | string | Snapshot trading date (YYYY-MM-DD) |
meta.structure_as_of | string | UTC source time represented by the structural snapshot; not the request time |
meta.flow_as_of | string | UTC update time for the session-flow summary; may equal structure_as_of before session flow is available |
meta.intraday_gex_as_of | string | null | UTC update time for the intraday GEX summary, or null when unavailable |
Symbol metadata
Volatility values are annualized decimals where applicable (0.22 means 22%); IV rank and percentile are 0–1 fractions. Nullable fields are null when unavailable. The optional realized-volatility fields may also be absent on older snapshots.
| Field | Type | Description |
|---|---|---|
underlying_type | STOCK | ETF | INDEX | null | Underlying classification |
description | string | Company, fund, or index name |
exchange / sector | string | null | Exchange and sector classification |
market_cap | number | null | Underlying market capitalization in USD |
outstanding_shares | number | null | Outstanding share count |
earning_date | string | null | Earnings date, when available |
average_stock_volume | number | null | Average underlying trading volume |
volume | number | Underlying volume in the symbol metadata; not option-contract volume |
open / high / low / close / last | number | Underlying prices in USD from the latest completed session when the structure was built. During a session this is the previous session; use intraday_gex.spot for the latest available intraday price |
previous_close | number | null | Previous underlying closing price in USD |
historical_volatility | number | null | Historical-volatility context; use rv20 or rv30 for an explicitly defined return window |
rv20 / rv30 | number | null, optional | Annualized sample realized volatility from the latest 20 / 30 valid trading-session log returns, using 252 trading days per year; null when there are insufficient returns |
rv_as_of | string | null, optional | Latest completed session included in RV20/RV30, as YYYY-MM-DD |
iv30 | number | null | Interpolated 30-day ATM implied volatility (decimal) |
iv_rank_1y / iv_percentile_1y | number | null | One-year IV rank / percentile as 0–1 fractions |
skew_25d_30d | number | null | 30-day 25-delta put/call skew |
butterfly_25d_30d | number | null | 30-day 25-delta volatility butterfly |
iv_term_slope_30_90 | number | null | 30-to-90-day IV term slope |
vol_date | string | null | Trading date of the IV analytics, as YYYY-MM-DD |
structure highlights
| Field | Type | Description |
|---|---|---|
spot | number | Underlying price used for exposure calculations |
call_oi / put_oi | number | All-scope open-interest contract totals |
call_gex / put_gex | number | All-scope dollar GEX for a 1% move; put GEX is signed negative |
scopes | object | Levels by DTE bucket, keyed all, zero_dte, weekly, and monthly. Each value is a levels object or null |
max_pain_curve | array | { strike, payout } points: the multiplier-adjusted aggregate payout at each candidate strike. Its lowest point is scopes.all.max_pain |
expirations | array | Full-chain strike aggregates grouped by expiration |
Days to expiration are calendar days measured from meta.effective_date: all includes every unexpired expiration, zero_dte is 0 days, weekly is 1–7 days, and monthly is 8–31 days. These are day ranges, not exchange expiration-series labels. A scope with no applicable contracts is null.
Each expirations entry contains expiration_date (YYYY-MM-DD) and a strikes array. Each strike contains numeric strike, call_oi, put_oi, call_gex, and put_gex; put GEX is signed negative. OI totals are contract counts and GEX is in USD for a 1% underlying move.
structure.scopes.<scope> levels
Levels are only inside a scope, for example data.structure.scopes.all.gamma_flip. structure has no top-level gamma_flip, max_pain, or wall fields.
| Field | Type | Description |
|---|---|---|
gamma_flip | number | null | Nearest repriced zero-net-GEX underlying level |
max_pain | number | null | Strike with lowest multiplier-adjusted aggregate payout |
call_gex_wall / put_gex_wall | number | null | Strongest call/put GEX strike on the corresponding side of spot |
call_oi_wall / put_oi_wall | number | null | Highest call/put OI strike on the corresponding side of spot |
flow highlights
| Field | Type | Description |
|---|---|---|
call_premium / put_premium | number | Session option premium by side (USD) |
bullish_dex / bearish_dex | number | Bullish and bearish delta-exposure flow totals |
trade_count | number | Option trades represented by the overlay |
traded_contract_count | number | Distinct contracts with trades |
intraday_gex highlights
| Field | Type | Description |
|---|---|---|
spot | number | Latest positive underlying price used for the summary |
call_gex | number | Full-chain positive call dollar GEX, including zero-trade contracts |
put_gex | number | Full-chain signed-negative put dollar GEX, including zero-trade contracts |
Semantics notes
- Dealer positioning is a model, not reported dealer inventory. Put GEX uses a documented dealer-short-put signing convention.
- Net GEX can be derived as
call_gex + put_gex(put already signed). - Session flow and
data.intraday_gexnormally update about every five minutes during the regular session. Usemeta.flow_as_ofandmeta.intraday_gex_as_ofseparately frommeta.structure_as_of; this cadence does not promise fresh values for every symbol on every interval. - Structural strike GEX, walls, Gamma Flip, Max Pain, OI, and expiration aggregates update when a newer full-chain structural snapshot becomes available. They do not follow each trade update in the Option Chain API.
data.intraday_gexisnullwhen no summary is available for the symbol. Its timestamp describes the summary update across the response's session, not a per-contract observation time. A zero flow total alone is not a completeness indicator.- Structural snapshots include unexpired supported standard contracts, including contracts with no trades. A root without supported structure data can return
404even when the Option Chain API has contract rows for it. - Put/call OI ratio can be derived as
put_oi / call_oiwhencall_oi > 0. - Prefer Market Structure for positioning dashboards; use the Option Chain API when you need filterable contract-level quotes and Greeks.
Errors
| Status | Meaning |
|---|---|
400 | Invalid symbol, or a date that is not a real YYYY-MM-DD calendar date |
401 | Missing or invalid API key |
403 | No active/trialing realtime entitlement |
404 | SNAPSHOT_NOT_FOUND for a date with no snapshot, or SYMBOL_NOT_FOUND for a symbol with no data |
405 | METHOD_NOT_ALLOWED: use GET; the Allow header lists the supported methods |
429 | Rate limited |
503 | UPSTREAM_UNAVAILABLE: the snapshot service or API key verification is unavailable or timed out; retry later, honoring Retry-After when present |
500 | Unexpected server error |
Errors return { "error": { "code": "...", "message": "..." } }. A 403 (SUBSCRIPTION_REQUIRED) also carries reason and billing_url inside error:
{
"error": {
"code": "SUBSCRIPTION_REQUIRED",
"message": "An active or trialing subscription is required.",
"reason": "NO_ACTIVE_SUBSCRIPTION",
"billing_url": "/billing"
}
}reason is always NO_ACTIVE_SUBSCRIPTION: the API key is valid, but its account has no active or trialing Pro subscription. A Pro subscription whose renewal payment failed more than 7 days into the unpaid billing period gets the same response until the invoice is paid. billing_url is the path on https://www.optiondata.io where the account owner can restore Pro access. Do not retry until access is restored.
FAQ
Q: Rate limits and access?
A: Requires trialing or active Pro + API key. The current default is about 60 requests / 60 seconds per customer on this endpoint, for trial and paid users, enforced on a best-effort basis. HTTP 429 includes Retry-After.
Planned, not yet active: trial 30/minute and 1,200/hour; paid Pro 60/minute and 3,600/hour. Once enabled, both limits apply per customer across their keys and IPs; reaching either triggers rate limiting. No effective date has been announced. Enterprise limits are contract-specific. See API rate limits for the complete policy. Subscription access remains the same as Option Chain and Historical SQL.
Q: Freshness?
A: Structural levels update on builds (structure_as_of). Some intraday GEX fields can refresh about every five minutes in session—use response meta timestamps.
Q: History range?
A: Dated snapshots cover recent retained trading sessions; availability varies by date and option root, and older sessions age out over time. Request the required date explicitly and handle 404 when it is unavailable. A dated snapshot is session-level data, not an intraday replay. For individual trades, Historical SQL covers the past 15 days on paid plans.
Q: Free trial / extension?
A: Complete qualification, then explicitly activate the 14-day Pro trial when you are ready. Eligible trial users can receive 50% off for life with a promotion code from Sales; contact Sales for the code, then enter it on Billing before the trial ends. Trials are not auto-extended.
Q: Market Structure vs Option Chain?
A: Structure for GEX/walls/max pain dashboards; Option Chain for filterable contract quotes/Greeks.
Related
- Product page: /market_structure
- Option chain API: /docs/option-chain-api/
- Realtime flow: /docs/realtime-option-trades-api/
Ask ChatGPT or Claude Code
Copy this prompt, paste it into ChatGPT, Claude, Claude Code, Cursor, or Codex, then add your question. It tells the model to read our public docs first — no API key needed for that step.
You are helping me use OptionData (https://www.optiondata.io/), an OPRA-licensed U.S. equity options data API.
Before answering, fetch these public files (no login required) and treat them as the source of truth:
- https://www.optiondata.io/llms.txt — short product map (same content as https://www.optiondata.io/llm.txt)
- https://www.optiondata.io/llms-full.txt — full API reference
- https://www.optiondata.io/openapi.json — HTTP OpenAPI
Do not invent endpoints, fields, tables, or limits. Prefer `Authorization: Bearer apikey_…` for HTTP APIs. Realtime uses `wss://ws.optiondata.io` with a `token` query parameter.
Products:
- Realtime trades WebSocket: wss://ws.optiondata.io
- Historical SQL: POST https://www.optiondata.io/api/historical/sql
- Option chain: POST https://www.optiondata.io/api/option-chain
- Market structure: GET https://www.optiondata.io/api/v1/market-structure/{symbol}
I am asking about: Market Structure API (GEX)
- Markdown: https://www.optiondata.io/md/market-structure-api
- HTML docs: https://www.optiondata.io/docs/market-structure-api
My question: