Docs/Option Chain API
API referenceOption Chain API
POST /api/option-chain reference: full U.S. option chains with strikes, expirations, bid/ask, OI, IV, and Greeks.
On this page
Beta. This API is in beta testing. The request and response schema (fields, units, and semantics) may change.
The Option Chain API exposes per-contract chain rows for an underlying, for a given trading date:
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 generatedapikey_...token. If omitted, the signed-in session is used.date:YYYY-MM-DD. Defaults to the latest complete trading session. Checkmeta.trading_datefor the date returned; an explicit date is never substituted with another session. An impossible calendar date (for example2026-02-30) returns400; a date with no stored session (weekend, holiday, future, or outside retention) returns404.expiration_date:YYYY-MM-DD; must be a real calendar date.put_call:CALLorPUT.strike: exact strike price.strike_min,strike_max: inclusive strike bounds; either bound may be used alone.
Source and semantics
Responses include every listed contract that matches your filters, including contracts with no trades. During U.S. market hours the current session updates about every 90 seconds: contracts that trade receive newer quotes, Greeks, IV, volume, open interest, and last price. Contracts without a newer trade keep their chain values. meta.as_of is the newest update represented by the returned rows; it does not mean every contract was refreshed at that time. Prices are in USD; Greeks/IV are decimals (e.g. IV 0.22 = 22%).
A new session becomes the default only after its complete chain is available. Until then, requests without date return the previous session and report its date in meta.trading_date.
Response — core fields (always returned)
{ "data": [ ... ], "meta": { "trading_date": "YYYY-MM-DD", "as_of": "..." } }
| Field | Type | Notes |
|---|---|---|
option_symbol | string | OCC option symbol |
put_call | CALL | PUT | |
strike | number | Strike price |
expiration_date | string | YYYY-MM-DD |
bid | number | null | Best bid recorded with the contract's most recent trade in the session; null when the contract has not traded in the session |
ask | number | null | Best ask recorded with the contract's most recent trade in the session; null when the contract has not traded in the session |
last_price | number | null | Latest trade price |
open_interest | integer string | null | Open interest as a JSON decimal string |
open_interest_change | integer string | null | open_interest − prior-day OI; null when prior-day OI is unavailable |
volume | integer string | null | Daily volume as a JSON decimal string |
implied_volatility | number | null | Decimal; null when no model value is available |
delta / gamma / theta / vega | number | null | Greeks (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
| Field | Notes |
|---|---|
trading_date | Trading session returned, in YYYY-MM-DD format |
as_of | Most recent data update represented in the response, as a UTC ISO timestamp or null |
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.
| Status | Meaning |
|---|---|
400 | Invalid body or failed request validation, including an impossible calendar date |
401 | Could not resolve a customer (missing/invalid api_key and no session) |
403 | Resolved, but no active/trialing realtime subscription |
404 | SNAPSHOT_NOT_FOUND: no chain is stored for the requested date. SYMBOL_NOT_FOUND: no chain is stored for the symbol on that session |
405 | METHOD_NOT_ALLOWED: use POST; the Allow header lists the supported method |
422 | Request was too broad for the response guardrail |
429 | Rate limited; honor Retry-After |
504 | Query timed out |
500 | Unexpected server error |
503 | Data is temporarily unavailable; retry with backoff |
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 the first year 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: