# Market 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.

> **Start here**
>
> Follow the [Market Structure quickstart](/docs/market-structure-quickstart), jump to the [request example](#example), or review the [response sections](#response-shape).

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.

```text
GET https://www.optiondata.io/api/v1/market-structure/:symbol
```

Included with the realtime plan (`trialing` or `active`). Authenticate with a signed API key.

## Authentication

Send your API key as:

```http
Authorization: Bearer YOUR_API_KEY
```

`YOUR_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

```bash
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_gex` normally update about every **five minutes** during the regular session. Use `meta.flow_as_of` and `meta.intraday_gex_as_of` separately from `meta.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_gex` is `null` when 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 `404` even when the Option Chain API has contract rows for it.
- **Put/call OI ratio** can be derived as `put_oi / call_oi` when `call_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`:

```json
{
  "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](/docs/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](/market_structure)
- Option chain API: [/docs/option-chain-api/](/docs/option-chain-api)
- Realtime flow: [/docs/realtime-option-trades-api/](/docs/realtime-option-trades-api)
