Back to blog

Historical SQL Now Covers a Rolling 15-Day Window: What Changed and How to Adapt

Paid Historical SQL queries the past 15 days of option trades, every row is at least 15 minutes old, and failed queries return stable error codes. Here is what changed and how to update your queries.

4 min readOptionData
historical-sqlapichangelogsql
RESEARCHOptionData blogSELECTWHEREGROUPORDER

Historical SQL has changed in three ways since August. Paid subscriptions now query a rolling 15-day window of option trades, every row the endpoint returns is at least 15 minutes old, and queries that fail now come back with stable error codes you can act on. This post walks through each change and what to update in your code. The dated record of each change lives in the API changelog.

Historical SQL at a glance

  • Paid window: past 15 days (360 elapsed hours)

  • Freshness: every row at least 15 minutes old

  • Rows per response: trial 10 · paid about 10,000

  • Window size: on the order of 100M trades - about 11M per session in recent weeks

The rolling 15-day window

Since September 12, 2026, paid (active) subscriptions query only the past 15 days of trades. The window is 360 elapsed hours, including weekends and holidays, so it usually spans about ten trading sessions. It moves continuously: a trade drops out exactly 360 hours after it executed.

The window is applied before your query runs. Older trades are excluded before joins, subqueries, and aggregations, even if your SQL has no date filter. Two consequences follow:

  • A date range that crosses the boundary returns only the trades inside the window.
  • A query that targets only older dates returns no rows, not an error. If a query that used to work now comes back empty, check its dates first.

Successful paid responses include meta.lookback_days: 15, so your code can confirm which window it was served. Trial access is unchanged: trial responses are capped at 10 rows.

Every row is at least 15 minutes old

Since August 10, 2026, Historical SQL returns only trades whose execution timestamp is at least 15 minutes old. The rule applies to every read of the trade table, including nested queries and aggregations, so you cannot reach newer rows through a subquery.

Each successful response reports meta.minimum_data_delay_minutes: 15 and the X-OptionData-Historical-Sql-Minimum-Data-Delay-Minutes header. For prints as they happen, use the realtime WebSocket.

Update your queries

Replace fixed dates with relative ones

Queries pinned to old calendar dates now return nothing on paid plans. Filter on recent relative dates instead, and keep the symbol filter early:

SELECT
  symbol,
  put_call,
  count() AS trades,
  sum(toFloat64(price) * size * 100) AS total_premium
FROM RawOptionTrades
WHERE symbol = 'AAPL'
  AND date >= today() - 7
GROUP BY symbol, put_call
ORDER BY total_premium DESC

Find the latest session cheaply

Use the small RawOptionTradesMaxDateOnlyMV view for the latest trading date. An unbounded max(date) over RawOptionTrades reads the whole table and fails with QUERY_TOO_BROAD.

SELECT max(date) AS latest_date
FROM RawOptionTradesMaxDateOnlyMV

Read the response meta

Check meta.entitlement, meta.row_limit, meta.capped, meta.lookback_days, and meta.minimum_data_delay_minutes before trusting a result. A trial response is capped at 10 rows; a paid response allows about 10,000.

Errors you can act on

Failed queries no longer surface database-internal messages or generic HTTP 500s. Each failure has a stable errorCode, and query problems include a reason:

errorCodeHTTPWhat it meansWhat to do
INVALID_QUERY422The SQL references an unknown column or function, mixes incompatible types, overflows a number, or is malformed. reason names the problem.Fix the query using reason.
QUERY_TOO_BROAD422Valid SQL that exceeded the scan limit (reason: SCAN_LIMIT_EXCEEDED). The response includes hints.Narrow the date range, filter by symbol, or split the window. Don't retry it unchanged.
QUERY_TIMEOUT504The query ran out of time.Narrow it the same way.
INTERNAL_ERROR500An unexpected service failure.Contact support with the X-Request-Id response header.

The INVALID_QUERY reasons are UNKNOWN_IDENTIFIER, UNKNOWN_FUNCTION, TYPE_MISMATCH, NUMERIC_OVERFLOW, SYNTAX_ERROR, INVALID_AGGREGATION, INVALID_ARGUMENTS, and INVALID_EXPRESSION:

{
  "status": "ERROR",
  "errorCode": "INVALID_QUERY",
  "reason": "UNKNOWN_IDENTIFIER",
  "errorMsg": "The query references a column, alias, or table identifier that is not available."
}

NUMERIC_OVERFLOW usually comes from multiplying fixed-precision columns. Cast before you multiply or aggregate, as in sum(toFloat64(price) * size * 100) above.

What did not change

The RawOptionTrades schema, the SELECT-only rules, and the endpoint are the same. Authorization: Bearer YOUR_API_KEY is the preferred way to authenticate, and the api_key body field is still accepted. Standard Pro access does not include trades older than the window; a separately scoped export needs a written agreement, so contact Sales through Support.

Where to go next


Run it with the OptionData API. Sign up, use the Support page's QR contact module to request the invitation required for qualification, then explicitly activate the 14-day no-card 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. One key covers Realtime WebSocket, Historical SQL, Option Chain REST, and Market Structure.

OptionData API

Run this with the OptionData API — one Pro key covers Realtime WebSocket, Historical SQL, Option Chain, and Market Structure.

Run this strategy with the OptionData API
Use Realtime WebSocket, Historical SQL, Option Chain, and Market Structure under one Pro API key.
curl -X POST https://www.optiondata.io/api/historical/sql \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "sql=SELECT * FROM RawOptionTrades WHERE date = (SELECT max(date) FROM RawOptionTrades WHERE symbol = 'AAPL' AND date >= today() - 7) AND symbol IN ('SPY', 'AAPL') ORDER BY time DESC LIMIT 10"