Run SQL directly against a full US market-data warehouse. Equities minute bars and tick data
from 2003, options trades from 2014, options NBBO quotes from 2022, plus corporate actions,
SEC filings, short interest, fundamentals, Treasury yields and macro series. It all lives in
one schema, so you can join across datasets in a single query.
There is a single data endpoint. You send SQL, you get rows. There is no per-dataset
endpoint to learn, no pagination cursor scheme, and no client library required.
Quickstart
Create a key in the terminal (https://ai.strasmore.com → API keys),
then send a query. The key is displayed once: we store only its SHA-256 hash and
cannot show it to you again.
The response is JSON. Rows come back as objects keyed by column name.
Authentication
Every endpoint except /v1/health takes a bearer token:
Authorization: Bearer sk_live_…
Keys resolve to an organization, and limits are applied per organization, not
per key. Minting extra keys does not raise your rate limit. Revoking a key takes effect
immediately.
Run a query
POST/v1/queryrequires key
Execute one read-only SELECT (or WITH … SELECT) against global_markets.
Body
Field
Description
sqlrequired string
One read-only statement. Maximum 100,000 characters.
Response
Field
Description
columns string[]
Column names, in result order.
rows object[]
One object per row, keyed by column name. Not positional arrays.
row_count integer
Rows returned, after any truncation.
truncated boolean
True if the result hit the 20,000-row cap. The result is truncated, not
errored — narrow the query, aggregate, or page with LIMIT/OFFSET.
elapsed float
Server-side execution time, in seconds.
generated_sql string | null
Always null today. Reserved so that adding natural-language querying later is
additive rather than a breaking change.
Fetch the schema
GET/v1/schemarequires key
Every table and column you can query, read from the warehouse itself at request time — so it
can never drift from what your query will actually see. It reflects your tier:
tables you cannot query are flagged, each table names the column its history window filters on,
and columns with known data defects carry a caveat.
Health
GET/v1/healthno auth
Liveness only. Returns {"status": "ok"}. Does not check the warehouse.
SQL restrictions
Your SQL runs under a read-only database user, behind a gate that requires:
Exactly one statement, and it must be a SELECT or a WITH … SELECT.
Multiple statements are rejected with a 400 — we will not silently execute the first and drop the rest.
No SETTINGS clause. It is stripped: it could otherwise override the server-side caps.
No table functions (remote, url, merge, file, …), anywhere, including inside subqueries.
No system.*, and no database other than global_markets.
No DDL or DML of any kind.
A semicolon inside a string literal is fine (WHERE name = 'Smith; Jones'), and so is a single
trailing semicolon. The engine is ClickHouse, so its SQL dialect and functions apply.
Limits
Limit
Value
Result rows
20,000 per query. Beyond this the result is truncated, not errored —
check the truncated flag.
Query timeout
60 seconds, enforced server-side.
Request body
100,000 characters of SQL. Larger returns 422.
Free-tier daily cap
100 queries/day, in addition to the rate limit.
Rate limits
Requests per minute, applied per organization. Exceeding it returns 429.
Tier
Requests / minute
free
10
starter
60
pro
300
quotes
600
enterprise
1200
Error contract
Every error is a JSON object with a single human-readable field:
{"detail": "…"}. A query that returns zero rows is a 200, not a 404.
Status
Meaning
400
The SQL was rejected by the gate (not a single read-only SELECT, a forbidden table, a
dataset your tier cannot access), or it failed to execute (syntax, unknown column).
401
Missing, malformed, unknown, or revoked API key.
422
Malformed request body — sql missing, or over 100,000 characters.
429
Rate limit exceeded, or the free-tier daily cap is reached. The message says which.
500
Internal error. Details are logged server-side and never returned to you.
History windows
Each tier sees a uniform depth of history across all datasets. The window is enforced
server-side, on the column named by history_column in the schema catalogue.
This is not an error
Rows older than your window are simply absent from results. You will not get a 400;
you will get fewer rows. If a historical query returns less than you expect, check your tier's
window before you check your SQL.
Tier
History
free
2 years
starter
5 years
pro
Full history
quotes
Full history
enterprise
Full history
Schema catalogue
36 tables in global_markets, read live from the warehouse.
Query them as global_markets.<table>. Tables marked
paid are unavailable on the free tier.
Equities — prices
delayed_stocks_minute_aggs9 columnsMinute OHLCV bars for US equities. The main price table. `window_start` is UTC.
cache_stocks_quotes15 columnspaid tierTick-level equity NBBO quotes. Paid tiers. From 2022.
history window applies to: sip_timestamp
tickerString
sip_timestampDateTime64(9)
participant_timestampDateTime64(9)
trf_timestampNullable(DateTime64(9))
sequence_numberInt64
ask_priceDecimal(12, 4)
bid_priceDecimal(12, 4)
ask_sizeInt32
bid_sizeInt32
ask_exchangeInt16
bid_exchangeInt16
tapeInt8
conditionsArray(Int16)
indicatorsArray(Int16)
_ingest_timeDateTime
Options
options_greeks18 columnspaid tierDaily Black-Scholes IV + greeks per contract (EOD close, delta/gamma/vega/theta/rho). One row per contract per session, 2014 onward, refreshed nightly. Paid tiers.
history window applies to: date
dateDate
tickerString
underlying_symbolString
option_typeString
strike_priceFloat64
expiration_dateNullable(Date)
volumeUInt64
option_closeFloat64
underlying_closeFloat64
risk_free_rateNullable(Float64)
days_to_expiryNullable(Float64)
implied_volatilityNullable(Float64)Solved from the EOD option close by bisection. Screen implied_volatility > 0.02 to drop degenerate fits.
iv_convergedNullable(UInt8)Filter iv_converged = 1: rows where the IV solve did not converge carry NULL greeks (deep ITM/OTM or stale closes).
deltaNullable(Float64)
gammaNullable(Float64)
vegaNullable(Float64)
thetaNullable(Float64)
rhoNullable(Float64)
options_trades13 columnspaid tierTick-level options trades (OPRA). Paid tiers. From 2014.
history window applies to: sip_timestamp
tickerLowCardinality(String)
sip_timestampDateTime64(9)
participant_timestampDateTime64(9)
priceDecimal(10, 4)
sizeUInt32
exchangeUInt8
conditionsArray(UInt16)
correctionUInt8
underlying_symbolLowCardinality(String)
expiration_dateDateUnreliable. Parse the expiry from the option ticker instead.
strike_priceDecimal(18, 3)
option_typeLowCardinality(String)
_ingest_timeDateTime
cache_options_quotes10 columnspaid tierTick-level options NBBO quotes. Paid tiers. From 2022. The largest table by far.
history window applies to: sip_timestamp
tickerLowCardinality(String)
sip_timestampDateTime64(9)
sequence_numberInt64
ask_priceDecimal(10, 2)
bid_priceDecimal(10, 2)
ask_sizeInt32
bid_sizeInt32
ask_exchangeInt16
bid_exchangeInt16
_ingest_timeDateTime
options_minute_aggs9 columnsMinute OHLCV bars per options contract.
history window applies to: window_start
tickerLowCardinality(String)
window_startDateTime
openDecimal(10, 4)
closeDecimal(10, 4)
highDecimal(10, 4)
lowDecimal(10, 4)
volumeUInt64
transactionsUInt32
_ingest_timeDateTime
Corporate actions
stocks_dividends13 columnsDeclared dividends: ex-date, record date, pay date, cash amount.
history window applies to: ex_dividend_date
ex_dividend_dateDate
tickerString
idString
cash_amountNullable(Float64)
currencyString
declaration_dateNullable(Date)
pay_dateNullable(Date)
record_dateNullable(Date)
frequencyNullable(Int32)
distribution_typeString
split_adjusted_cash_amountNullable(Float64)
historical_adjustment_factorNullable(Float64)
_ingest_timeDateTime
stocks_splits8 columnsStock splits (forward and reverse) with execution date and ratio.
stocks_income_statements35 columnsQuarterly/annual income statements.
history window applies to: filing_date
period_endDate
filing_dateDateUNRELIABLE. Vendor defect: ~89% of quarterly rows carry a filing_date more than 200 days after period_end (older fiscal years are stamped with recent years' dates). Use period_end as the time key.
Known defects in the underlying data. We publish them rather than let you discover them: a
silently wrong number is worse than a missing one. They are also attached to the affected
columns in GET /v1/schema.
options_greeks.iv_converged
Filter iv_converged = 1: rows where the IV solve did not converge carry NULL greeks (deep ITM/OTM or stale closes).
options_greeks.implied_volatility
Solved from the EOD option close by bisection. Screen implied_volatility > 0.02 to drop degenerate fits.
options_trades.expiration_date
Unreliable. Parse the expiry from the option ticker instead.
stocks_balance_sheets.filing_date
PARTLY UNRELIABLE (~43% of rows filed >200d after period_end). Prefer period_end.
stocks_cash_flow_statements.filing_date
UNRELIABLE (same vendor defect as stocks_income_statements). Use period_end.
stocks_income_statements.filing_date
UNRELIABLE. Vendor defect: ~89% of quarterly rows carry a filing_date more than 200 days after period_end (older fiscal years are stamped with recent years' dates). Use period_end as the time key.
stocks_short_volume.date
This table can contain duplicate rows for a ticker/date. Dedupe with GROUP BY + max().