Skip to content

Docs/Option Chain API

API reference

Option Chain API

POST /api/option-chain reference: full U.S. option chains with strikes, expirations, bid/ask, OI, IV, and Greeks.

Open product
On this page

The Option Chain API exposes per-contract chain rows for an underlying, for a given trading date:

Text
POST https://www.optiondata.io/api/option-chain

It is included with the realtime plan — both trialing and active realtime subscriptions get full access.

Authentication

Send Authorization: Bearer YOUR_API_KEY for API clients. The header takes precedence over the legacy body api_key; malformed authorization is rejected. If neither is supplied, the portal can use the signed-in session. Live access requires an active or trialing Pro subscription.

Request

JSON and form (application/x-www-form-urlencoded / multipart/form-data) bodies are supported. Field names are snake_case.

Required:

  • symbol: one underlying ticker (uppercased by the server; 1–16 chars).

Optional:

  • api_key: cus_... (raw Stripe customer id) or a generated apikey_... token. If omitted, the signed-in session is used.
  • date: YYYY-MM-DD. Defaults to the latest available trading session. Check meta.trading_date for the date returned; an explicit date is never substituted with another session. An impossible calendar date (for example 2026-02-30) returns 400; a date with no stored session (weekend, holiday, future, or outside retention) returns 404.
  • expiration_date: YYYY-MM-DD; must be a real calendar date.
  • put_call: CALL or PUT.
  • strike: exact positive strike price; cannot be combined with strike_min or strike_max.
  • strike_min, strike_max: inclusive strike bounds; either bound may be used alone.

Strike bounds must be positive, and strike_min must not exceed strike_max.

Source and semantics

Responses include the complete available chain inventory matching your filters, including contracts with no trades. During U.S. market hours, newer trade-derived values are normally incorporated about every 90 seconds: quotes, Greeks, IV, volume, open interest, and last price. Contracts without a newer trade retain their chain values until a newer chain snapshot is available. This cadence is an expected update interval, not a guarantee that every contract's quotes or Greeks refresh on each interval. Prices are in USD; Greeks/IV are decimals (e.g. IV 0.22 = 22%).

Before a new session's chain is available, requests without date can return the previous session. Always check meta.trading_date instead of assuming that the latest available session is today's date.

If intraday updates are temporarily unavailable, a response can retain earlier chain values. Check both meta.trading_date and meta.as_of before treating a response as current. An explicit historical date selects that session's stored chain; it does not replay the chain at a particular time within the session.

Response — core fields (always returned)

{ "data": [ ... ], "meta": { "trading_date": "YYYY-MM-DD", "as_of": "..." } }

FieldTypeNotes
option_symbolstringOCC option symbol
put_callCALL | PUT
strikenumberStrike price
expiration_datestringYYYY-MM-DD
bidnumber | nullBest bid recorded with the contract's most recent trade in the session; null when the contract has not traded in the session
asknumber | nullBest ask recorded with the contract's most recent trade in the session; null when the contract has not traded in the session
last_pricenumber | nullLatest trade price
open_interestinteger string | nullOpen interest as a JSON decimal string
open_interest_changeinteger string | nullopen_interest − prior-day OI; null when prior-day OI is unavailable
volumeinteger string | nullDaily volume as a JSON decimal string
implied_volatilitynumber | nullDecimal; null when no model value is available
delta / gamma / theta / veganumber | nullGreeks (decimal); null together with implied_volatility when no model value is available

Numeric fields are null when the value is unavailable for a contract. bid and ask come from the quote attached to the contract's latest trade in the meta.trading_date session, so a contract that last traded early in the day carries that earlier quote. bid and ask are null together, and implied_volatility and the Greeks are null together. A single 0, such as a bid of 0 with a positive ask, is a real value. This endpoint returns chain fields only; use the realtime WebSocket for flow fields such as premium, size, and trade count.

open_interest, open_interest_change, and volume are serialized as decimal strings so that 64-bit values stay exact. Parse them with an integer type that is safe in your language; do not assume every 64-bit integer can be represented exactly by a JavaScript number.

Response — meta

FieldNotes
trading_dateTrading session returned, in YYYY-MM-DD format
as_ofNewest contributing chain snapshot time or merged trade time, as a UTC ISO timestamp or null. This is a response-level freshness signal, not the request time or a per-contract quote/Greek timestamp

Errors

All errors return { "error": { "code": "...", "message": "..." } } with an HTTP status. A chain that exists but has no contract matching your filters is not an error: it returns 200 with an empty data array.

StatusMeaning
400Invalid body or failed request validation, including an impossible calendar date
401Could not resolve a customer (missing/invalid api_key and no session)
403Resolved, but no active/trialing realtime subscription
404SNAPSHOT_NOT_FOUND: no chain is stored for the requested date. SYMBOL_NOT_FOUND: no chain is stored for the symbol on that session
405METHOD_NOT_ALLOWED: use POST; the Allow header lists the supported method
422Request was too broad for the response guardrail
429Rate limited; honor Retry-After
504Query timed out
500Unexpected server error
503UPSTREAM_UNAVAILABLE: data or API key verification is temporarily unavailable; retry after the Retry-After seconds, with backoff

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 customer was resolved, but 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: How often should I poll?
A: The latest session updates about every 90 seconds during market hours, so poll every one to two minutes at most and compare meta.as_of before doing new work. A contract without a recent trade keeps its last chain value, so an update does not refresh every Greek. Faster polling returns the same data and may hit rate limits.

Q: Rate and size limits?
A: Keep sustained traffic at one request per second per customer and short bursts to 10 requests or fewer, for example when fetching several expirations. Current throttling is best-effort; this is client usage guidance, not a guaranteed global quota. HTTP 429 includes a Retry-After header; wait that long before retrying. See API rate limits for the complete policy.

Planned, not yet active: trial 20/minute and 600/hour; paid Pro 60/minute and 3,000/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.

Response-size behavior is unchanged: over-broad chains are rejected instead of returning partial results—use expiration, put/call, or strike filters.

Q: History range and delay?
A: Chain sessions from about 2026-02-20 forward (grows each trading day). Use as_of for freshness. For individual trades, use Historical SQL: paid access covers the past 15 days (≥15-minute delay).

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: WebSocket full chain?
A: No. Chains are REST only; WebSocket is trades/flow.

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: Option Chain API
- Markdown: https://www.optiondata.io/md/option-chain-api
- HTML docs: https://www.optiondata.io/docs/option-chain-api

My question: