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:
errorCode | HTTP | What it means | What to do |
|---|---|---|---|
INVALID_QUERY | 422 | The 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_BROAD | 422 | Valid 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_TIMEOUT | 504 | The query ran out of time. | Narrow it the same way. |
INTERNAL_ERROR | 500 | An 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
RawOptionTradesschema, the SELECT-only rules, and the endpoint are the same.Authorization: Bearer YOUR_API_KEYis the preferred way to authenticate, and theapi_keybody 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
- The Historical Option Trades API reference has the schema, limits, and every error shape.
- The Historical SQL quickstart gets a first query running in a few minutes.
- The Option Chain API returns full chains for any retained session, including ones older than the 15-day trade window.
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.
Run this with the OptionData API — one Pro key covers Realtime WebSocket, Historical SQL, Option Chain, and Market Structure.
-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"