Skip to content

Docs/Realtime Option Trades API

API reference

Realtime Option Trades API

WebSocket API reference for OPRA-licensed U.S. equity option trades: filters, RAW vs AGGREGATED modes, fields, test mode, and connection limits.

Open product
On this page

The Realtime Option Trades API provides streaming access to option trade data.

WebSocket endpoint

Text
wss://ws.optiondata.io

Request

Parameters

NameExampleRequiredDefaultDescription
tokenYOUR_API_KEYRequired—Get your API key here: optiondata.io
aggregation_modeAGGREGATED or RAWOptionalAGGREGATEDDetermine how OptionData groups trades. AGGREGATED combines prints executed simultaneously for the same option symbol. RAW emits one service record per received print without OptionData trade aggregation; it is not an OPRA-native audit feed or a completeness guarantee.
symbolsSPY,TSLA,SPX or AAPLOptionalAll symbolsA list of symbols in upper case separated by comma. Using the * wildcard or don't provide it to subscribe to all symbols.
premium[100000,250000] or [100,null]Optional[0,null]The filter for minimum premium. The first value represents the minimum premium, while the second value indicates the maximum premium. If not provided, [0,null] will be the default value.
sentimentBULLISH,BEARISH,NEUTRAL or BULLISHOptionalAll sentimentsThe filter for sentiment includes potential values of BULLISH, BEARISH, and NEUTRAL. You can combine multiple selections by separating them with commas, such as NEUTRAL,BEARISH.
underlying_typeSTOCK,ETF,INDEX,ETN,REITOptionalAll typesThe filter for underlying_type includes potential values of STOCK, ETF, INDEX, ETN, and REIT. You can combine multiple selections by separating them with commas, such as INDEX,STOCK.
delta[-0.9,0.98]Optional[-1,1]The filter for delta has a default range of [-1, 1]. The first value represents the minimum delta, while the second value indicates the maximum delta. The range must remain within [-1, 1]
moneynessITM,OTM,ATMOptionalAll moneynessThe filter for moneyness includes potential values of ITM, ATM, and OTM. You can combine multiple selections by separating them with commas, such as ITM,OTM.
expiry_days[15,20]OptionalNo filteringThe days remaining until the expiration date. The first number represents the minimum expiration days, with a default value of 0 if null is provided. The second number is the maximum expiration days; if null is provided, there is no limit.
expiration_date2026-10-17 or 2026-10-17,2026-10-24OptionalNo filteringExact option expiration dates in YYYY-MM-DD, comma-separated for several. A record passes when its expiration_date equals one of them. Unlike expiry_days, the selection does not move from one session to the next, so combine it with symbols, put_call and strike to follow one contract. Dates not in that format match nothing.
is_recent_earning_onlytrueOptionalNo filteringOnly trades on stocks with upcoming earnings. Aggregated only
put_callCALL or PUTOptionalNo filteringFilter to identify whether this option trade is PUT or CALL
sideASK,AASK,BID,BBID,MIDOptionalNo filteringPrice side of the trade. Values: AASK (above ask), ASK, MID, BID, BBID (below bid). Comma-separated for multiple.
strike[15.5,20]Optional[0,null]Identity the range of the Strike price of the option trade. The first number represents the minimum strike price, with a default value of 0 if null is provided. The second number is the maximum strike price. if null is provided, there is no limit at the maximium strike price. default value is [0,null] with minimum strike price is $0, and no limit on the maximum strike price.
oi[155,2000]Optional[0,null]The total number of this options contract that are still open. The first number represents the minimum OI, with a default value of 0 if null is provided. The second number is the maximum OI. if null is provided, there is no limit at the maximium OI. default value is [0,null] with minimum OI is 0, and no limit on the maximum OI.
iv[0.1,1.1]Optional[0,null]Implied Volatility which reflects the market's expectations of the future volatility of the underlying asset's price. The first number represents the minimum IV, with a default value of 0 if null is provided. The second number is the maximum IV. if null is provided, there is no limit at the maximium IV. default value is [0,null] with minimum IV is 0, and no limit on the maximum IV.
option_activity_typeAUTOOptionalNo filteringOPRA trade condition code. Values: AUTO (AutoExecution), SLAN (SingleLegAuctionNonISO), MLET (MultiLegAutoEx), MLAT (MultiLegAuction), ISOI (IntermarketSweep), MESL (MultiLegAutoSingleLeg), TLET (StockOptionAutoEx), SLFT (SingleLegFloor), MLFT (MultiLegFloor), CBMO (MultiLegFloorPropProduct). Aggregated only
trade_count[1,1] or [2,null]OptionalNo filteringNumber of trades involved in the sweep or trade. Use [1,1] to filter for block trades only, and [2,null] to filter for aggregated trades. In RAW mode, this is always 1.
is_opening_onlytrueOptionalNo filteringOnly opening trades (2 × size >= OI + daily volume). Aggregated only
size[155,2000]Optional[0,null]Filter for the total order size. The first number represents the minimum size, with a default value of 0 if null is provided. The second number is the maximum size if null is provided, there is no limit at the maximium size. default value is [0,null] with minimum size is 0, and no limit on the maximum size.
dex[100,500]OptionalNo filteringThe filter for Delta Exposure (DEX). The first value represents the minimum dex, while the second value indicates the maximum dex. If not provided, default is no filtering.
dei[0.1,0.5]OptionalNo filteringThe filter for Delta Impact (DEI). The first value represents the minimum dei, while the second value indicates the maximum dei. If not provided, default is no filtering.
test_modetrueOptionalDisabledEnable test snapshot mode: server sends pre-loaded sample data and then closes the connection. No authentication required. Use to validate filters and client parsing before going live.

Sample Code

TypeScript
import WebSocket from 'ws';

const url = "wss://ws.optiondata.io?symbols=AAPL,SPX&token=YOUR_API_KEY&premium=[300000,null]";

// Create a new WebSocket instance
const ws = new WebSocket(url);

// Event handler for connection open
ws.on('open', function open() {
  console.log('WebSocket connection opened');
});

// Event handler for incoming messages
ws.on('message', function incoming(data) {
  console.log('Received message:', data);
  // You can parse and handle the data here
  try {
    const receivedData = JSON.parse(data.toString());
    console.log('Parsed data:', receivedData);
    // Further processing of receivedData
  } catch (error) {
    console.error('Error parsing received data:', error);
  }
});

// Event handler for connection close
ws.on('close', function close() {
  console.log('WebSocket connection closed');
});

// Event handler for errors
ws.on('error', function error(err) {
  console.error('WebSocket error:', err);
});

Scroll to read the full example, or expand it. Copy includes all lines.

Response

Schema

NameTypeDescription
idstringIdentifier derived from the underlying symbol, date, trade time to the second, price, and size. It is not unique: prints that share those values share an id, even on different contracts, so do not use it to remove duplicates
symbolstringTicker Symbol (TSLA, MSFT, etc...)
datestringthe date the trade was executed, the date format is YYYY-MM-DD
timestringThe time the trade was created. Format is YYYY-MM-DD HH:MM:SS
put_callstringIndicates whether this trade is PUT or CALL
strikefloatStrike price of the option trade
expiration_datestringThe date on which the Option expires. The Option becomes invalid after this date and cannot be exercised ex: 2022-04-05
option_activity_typestringOPRA trade condition code (e.g., "AUTO", "SLAN"). Aggregated only
underlying_typestringIndicates underlying is Common Stock, ETF, ETN, etc.
oiintegerOpen Interest. The total number of this options contract that are still open.
sizeintegerTotal order size (either of the 1 trade for the trade of SINGLE or REPEATED, or the sum of trade sizes for a AGGREGATED trade)
pricefloatLast price of a trade, or last price of last trade in a sweep.
underlying_pricefloatCurrent stock price of the underlying asset
bidfloatOption contract best bid
askfloatOption contract best ask
ivfloatImplied volatility (IV) is an estimate of the future volatility of the underlying stock based on options prices.
premiumfloatCost in dollar of the entire sweep or block option trade
sentimentstringBULLISH, BEARISH, or NEUTRAL The sentiment is estimated based on whether the trade was executed at the bid, ask, or spot price.
trade_countintegerNumber of trades involved in the sweep. In RAW mode, this is always 1.
expiry_daysintegerThe days left to expiration date.
sidestringPrice side: "AASK" (above ask), "ASK", "MID", "BID", "BBID" (below bid)
moneynessstring"ITM" (in the money), "ATM" (at the money), "OTM" (out of the money)
deltafloatDelta is a measure of the change in an option's price
gammafloatOption gamma
dexnumberDelta exposure in shares: round(abs(delta) × size × 100). Always non-negative; direction comes from put_call and sentiment
deinumberDelta exposure as a percent of the underlying's average daily share volume: dex ÷ avg_daily_volume × 100
daily_volumeintegerDay volume for this option contract including this trade Aggregated only
earning_datestringNext earning date for the underlying asset Aggregated only
updated_timestampnumberUTC timestamp in milliseconds when this record was created
market_capnumberMarket capitalization in dollars (current share price × outstanding shares) Aggregated only
option_symbolstringOption Contract Symbol, for example: TSLA240430P00508000, this refers to a put option contract with a strike price of $508 and an expiration date of April 30, 2024.

Message format

When the WebSocket connects, the server sends a single connection success message. After that, each trade record is sent as an individual JSON message: the message body is the trade object itself (no status or data wrapper). Parse each incoming message as JSON; if it has a status field, treat it as control (e.g. SUCCESS); otherwise treat it as a trade record.

Sample response

When WebSocket connected (one-time):

JSON
{
  "status": "SUCCESS",
  "msg": "Connection established with id: 159b4576-70ec-476f-8a1d-b061fdca0fb8",
  "data": {
    "connection_id": "1707123456789-a1b2c3d4e5f6"
  }
}

When streaming data is received (each message is a plain trade object):

JSON
{
  "id": "13k7t4e",
  "date": "2025-05-02",
  "time": "2025-05-02 09:53:51",
  "symbol": "TSLA",
  "put_call": "CALL",
  "strike": 280,
  "expiration_date": "2025-05-02",
  "size": 7,
  "price": 4.44,
  "premium": 3109,
  "bid": 4.38,
  "ask": 4.5,
  "underlying_price": 281.77,
  "side": "MID",
  "sentiment": "NEUTRAL",
  "moneyness": "ITM",
  "expiry_days": 0,
  "option_symbol": "TSLA250502C00280000",
  "trade_count": 2,
  "dex": 413,
  "dei": 0.00239,
  "iv": 0.588,
  "delta": 0.589,
  "gamma": 0.0449,
  "oi": 14410,
  "daily_volume": 8352,
  "underlying_type": "STOCK",
  "option_activity_type": "SLAN",
  "earning_date": "2025-07-21",
  "market_cap": 900485935542,
  "updated_timestamp": 1746194031262
}

Scroll to read the full example, or expand it. Copy includes all lines.

Response Error

401 Unauthorized (missing or invalid token, before WebSocket upgrade)

JSON
{
  "status": "ERROR",
  "code": 401,
  "msg": "Unauthorized, please login to https://optiondata.io get a valid API subscription plan first"
}

500 Internal Error

JSON
{
  "status": "ERROR",
  "code": 500,
  "msg": "Internal Error"
}

Heartbeat

The server sends a ping message every 20 seconds to keep the connection alive:

JSON
{ "type": "ping", "ts": 1707123456789 }

Clients can optionally respond with a pong: { "type": "pong", "ts": 1707123456789 }. If the client sends {"type": "ping"}, the server responds with {"type": "pong", "ts": ...}.

Connection limits

Each API token is limited to 5 concurrent WebSocket connections. When the limit is reached, a new WebSocket/SSE handshake is rejected with HTTP 429 and a Retry-After header; existing connections remain open. Respect the retry delay and use exponential backoff rather than opening another connection immediately.

Test mode

Use test_mode=true to validate your integration without live market data. No token is required.

  • URL example: wss://ws.optiondata.io?test_mode=true&aggregation_mode=AGGREGATED&symbols=TSLA
  • After sending the connection success message, the server streams pre-loaded sample trade records (with your filters applied), then closes the connection with code 1000 and reason "test_snapshot_complete".
  • No heartbeat pings are sent in test mode. Connection limits are not enforced.

FAQ

What is an Aggregation Mode?

A: Real-time Option Trades in AGGREGATED mode consolidates trades with the same option symbol executed simultaneously (at the second level) into a single aggregate trade. This approach simplifies the identification of large trades that have been divided into multiple smaller transactions. If you want print-level service records without OptionData trade aggregation, use RAW mode. RAW is not an OPRA-native audit feed or a completeness guarantee. The major differences between the two modes are:

  • The trade_count for option trades in RAW mode is always 1. In AGGREGATED mode, the trade_count can be either 1 or greater than 1. If it is greater than 1, this indicates that the option trade record aggregates multiple original option trades with the same option symbol that was executed simultaneously.
  • The average daily volume for Real-time Option Trades in AGGREGATED mode is 4 million rows of records, while the average daily volume for RAW is around 7 million rows of records.

How to choose between AGGREGATED mode and RAW mode?

A: Institutional players often divide large trades into smaller ones and execute them simultaneously. They do this either to hide their intentions or due to a lack of liquidity or market depth at the moment of execution. This is the background for why we introduced AGGREGATED mode, for the case that you need to identify significant block trades in real-time, you can select AGGREGATED mode, allowing our aggregation services to combine these trades. If you prefer print-level service records without OptionData trade aggregation, choose RAW mode instead. By default, the API uses AGGREGATED mode unless you specify a different value for the aggregation_mode field. Please note that this is an assumption that trades executed simultaneously are sent by the same trader/institution, otherwise, you can choose RAW mode instead.

Which OPRA audit fields does RAW mode expose?

A: RAW means one service record per received print (trade_count = 1), without OptionData trade aggregation; it is not an OPRA-native audit feed. The portal payload does not expose the original OPRA trade condition, exchange, correction/cancellation status, or source sequence/message id. It exposes only the documented service fields, including a service-generated id and millisecond timestamps.

Does RAW mode guarantee every trade and a complete, lossless session?

A: No premium or size filter is applied when those parameters are omitted, and RAW records are not aggregated. However, OptionData does not warrant that every source print is delivered or that a session is complete, uninterrupted, or lossless. The stream has no source sequence field with which a client can independently certify completeness.

What connection, backlog, drop, and recovery telemetry is exposed?

A: Clients receive a connection-success control message, individual trade messages, heartbeat pings about every 20 seconds, transport close/errors, and HTTP 429 with Retry-After when the five-connection cap is exceeded. The API does not expose customer-facing backlog depth, drop counts, recovery counts, gap counters, or a complete-session marker. Track receive counts, time gaps, disconnects, and reconnects in your client.

Can I retain raw WebSocket frames or derived files after access ends?

A: Under the current Terms, when access or the subscription terminates you must stop using the Service and delete copies of raw Data. Derived files are permitted only when they cannot be reverse engineered to recreate the original Data or a reasonable substitute. Self-serve Pro is for individual, non-professional use; evaluation or use for a company, business, or professional purpose requires a separate commercial license from support@optiondata.io.

Can you give me some examples?

A: Let's say there are two records in real-time option trades with RAW mode like below:

Code
// RAW MODE
date        time      symbol  strike  put_call expiration_date premium size trade_count
2025-02-01  09:32:02  TSLA    330     CALL     2025-02-15       1000    2    1
2025-02-01  09:32:02  TSLA    330     CALL     2025-02-15       2000    3    1

These two trades will be aggregated into one trade since they are executed at the same time with the same option symbol.

Code
// AGGREGATED MODE
date        time      symbol  strike  put_call expiration_date premium size trade_count
2025-02-01  09:32:02  TSLA    330     CALL     2025-02-15       3000    5    2

For the example above, the size of trade in the AGGREGATED mode is equal to the sum of the size of the trades in the RAW mode(5 = 3 + 2), the trade size of trade in the AGGREGATED mode is equal to the sum of the trade_size in the RAW mode. For other data fields in AGGREGATED mode (eg: delta, iv, gamma, theta) is the average value of the related data field in the RAW mode.

Is there a delay in the data?

A: Realtime WebSocket trades are live during market hours. We typically process around 20,000 records per minute; the open may lag under surge.

Market open delay chartSelect the image to view it at full size.

What is the typical daily volume?

A: On the order of 10M+ prints/day. AGGREGATED mode averages about 4M records/day; RAW mode about 7M records/day.

What are connection and rate limits?

A: Each API token allows up to 5 concurrent WebSocket connections. A sixth handshake receives HTTP 429 with Retry-After; existing connections are not evicted. Heartbeats run about every 20 seconds. HTTP APIs (Historical SQL, Option Chain, Market Structure) default to about 60 requests per 60 seconds per key (HTTP 429 + Retry-After).

How to get started? Free trial length / extension?

A: Register at https://www.optiondata.io, complete qualification with an invitation code, then explicitly activate the 14-day no-card Pro trial when you are ready. Copy your API key and connect to wss://ws.optiondata.io. If you do not add a payment method, you are not charged and access does not continue as paid when the trial ends. 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.

What happens when the market closes?

A: During market closures, your authenticated live WebSocket connection stays open. Data streaming stops, but heartbeat pings continue every 20 seconds. When the feed resumes for the next applicable trading session, data resumes on the same connection; no reconnection is needed. If you connect outside market hours, authentication succeeds normally and you receive the connection success message, but no trade data is sent until the market opens. The portal offers separate Sample and Live modes. Sample mode returns a finite synthetic snapshot and closes. Live mode does not switch to sample based on the browser clock, weekends, or holidays. An open connection with no matching trades is a waiting state. For historical trade research, use the Historical SQL API.

What does the Pro plan include? Refunds?

A: One Pro subscription covers Realtime WebSocket, Historical SQL (trial SQL: past 7 days, up to 1,000 rows per response), Option Chain REST, and Market Structure API under one key. Eligible trial users can receive 50% off for life with a promotion code from Sales, with application steps shown inside the authenticated portal. 30-day money-back guarantee; cancel anytime. Refunds: email support@optiondata.io from your login address within 30 days of purchase.

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: Realtime Option Trades API
- Markdown: https://www.optiondata.io/md/realtime-option-trades-api
- HTML docs: https://www.optiondata.io/docs/realtime-option-trades-api

My question: