Crypto market data API — buy over HTTP, stream over WebSocket
The CryptoStruct crypto market data API is self-service: a keyless HTTP flow to find instruments, quote and buy tick-data days (machine-readable as OpenAPI 3.1), a WebSocket feed for live order books and trades, free statistics routes, and an MCP server for coding agents — €1 per instrument-day, no subscription, no account for a guest purchase.
Facts that do not change per call: one file = one instrument × one UTC day, €1 per instrument-day (prices are tax-inclusive EUR); day bundles (kind: series_day) are priced per series, mostly €1 per bundle-day; no account, subscription or API key is needed — a guest checkout only needs an email for the receipt. Card payment ALWAYS happens in the user's browser on a Stripe page: an agent never collects card details. Every endpoint is rate-limited per IP; on a 429 wait for the Retry-After seconds. Machine-readable description: OpenAPI 3.1 · site map for agents: llms.txt · terms · data license.
Find the instrument
GET https://cryptostruct.com/api/search?q=<text>&venue=<code|family>&class=<class>&base=<asset>&limit=25
q: ticker (BTCUSDT), pair, base asset, or a numeric instrument id. Venue and class WORDS insideqare understood (binance btcusdt perp,BTCUSDT-PERP,ETH options) and echoed ininterpretation.venue: a venue code (binance_swap) or a family (binance= every Binance venue,kraken,okx,gate,htx).class:spot | perpetual | future | call | put | prediction— aliasesperp,swap,futures,option(= call + put) are accepted; an unknown value is a 400 with the vocabulary.- Response:
{query, filters, total, results[{instrument_id, code, type, exchange{code,name}, state, data{days, first_day, last_day, total_bytes}}], interpretation?, did_you_mean?}. Zero hits come withdid_you_mean(nearest venue codes, what to drop). Useexchange.code+code+typeto confirm you have the right market before buying —BTCUSDTexists on many venues. - Free: 100 results per call. The
instrument_idis stable and identical across MCP, shop and analytics pages.
Example: curl -s 'https://cryptostruct.com/api/search?q=BTCUSDT&venue=binance&class=perp' → Binance USDT-M perpetual BTCUSDT = instrument_id 67824.
Which days exist
GET https://cryptostruct.com/api/instrument/{id}/coverage→{first_day, last_day, days_with_data, coverage_pct, gaps[{from,to,days}], total_bytes}(one call, use it to pick a window).GET https://cryptostruct.com/api/instrument/{id}/days?limit=100&before=YYYY-MM-DD→{days[{date, size}], next_cursor}, newest first — "last week" = the first 7 rows.sizeis the compressed (zstd) file size in bytes.GET https://cryptostruct.com/api/instrument/{id}/calendar?month=YYYY-MM→ the days of one month.- Day bundles (prediction series, option chains, futures curves):
GET https://cryptostruct.com/api/bundleslists every sellable series withbundle_id,series_key,price_cents, coverage;GET https://cryptostruct.com/api/bundle/{series_key}/days?limit=&before=lists its days. In quote and checkout a bundle day is{instrument_id: <bundle_id>, date, kind: "series_day"}. - Unknown and hidden ids answer 404
{error:{code:"NOT_FOUND"}}alike.
Days are UTC; the newest sellable day is normally yesterday (UTC). Only days that exist can be bought — the quote tells you which ones do not.
Quote
POST https://cryptostruct.com/api/quote with {"items":[{"instrument_id":67824,"date":"2026-09-30","kind":"tick"}, …]} (1–365 items, ≤ 20 distinct instruments/bundles, kind defaults to tick; no other keys).
Response: {items_requested, items_available, total_cents, total_eur, currency:"EUR", price_eur_per_day, available[{instrument_id,date,kind}], missing[{instrument_id,date,kind,reason}], bundles?, bundle_days?, vat_note, terms_url, checkout{method,url,headers,body_example,then[]}}. available is the checkout-ready item list; checkout is the next step spelled out. Keyless, 30 calls per minute per IP.
Checkout (guest)
POST https://cryptostruct.com/v1/checkout/session
- Headers:
Content-Type: application/json,Idempotency-Key: <uuid>(reuse the same key when retrying the same basket — never for a new one),X-CryptoStruct-Client: http(marks the order as an HTTP-agent order in our statistics; optional, never affects price or delivery). - Body:
{"email": "<buyer email>", "items": [<the available[] items from the quote>], "success_url": "https://cryptostruct.com/order/thanks?session_id={CHECKOUT_SESSION_ID}", "cancel_url": "https://cryptostruct.com/shop"}— return URLs must be on https://cryptostruct.com (anything else is a 400). - Response
201:{order_id, checkout_url, session_id, currency, item_count, subtotal_cents}. Opencheckout_urlfor the user (show it, don't fetch it): that is the Stripe page where they pay by card. The order is pending until then. - Errors: 400
INVALID_INPUT, 422FILE_NOT_AVAILABLE(details.missinglists the items — drop them and retry), 429 (checkout is limited per IP), 409ALREADY_OWNED(signed-in only). - Signed-in buyers (Clerk session / OAuth bearer) get credits and owned-file dedupe automatically; that path is the MCP server's job — plain HTTP agents use the guest flow above.
Poll until paid
GET https://cryptostruct.com/v1/checkout/session/{session_id} → {status:"pending"} while the user is on the Stripe page, then {status:"paid", order_id, download_token, invoice_hosted_url, invoice_pdf_url}. Poll every 10–15 seconds; a session the user abandons stays pending and simply expires — create a new checkout for a new attempt. 404 = unknown session.
The order
GET https://cryptostruct.com/v1/orders/{order_id} with Authorization: Bearer <download_token> → {order_id, status, paid_at, currency, total_cents, item_count, items[{instrument_id, code, exchange_code, type, date, kind, bytes, price_cents}], invoice_hosted_url, invoice_pdf_url}. A wrong token and an unknown order both answer 404 (no enumeration). After 30 days a guest order answers 410 ORDER_EXPIRED — download within that window, or buy with a signed-in account (never expires). The download_token is the only credential: treat it like a password, keep it out of logs and prompts.
Download
Per tick file: GET https://cryptostruct.com/api/download/order/{order_id}/{instrument_id}/{date}?t=<download_token> streams the .txt.zst file (zstd-compressed JSON lines; line 1 is the instrument master data; zstd -d to inflate). Files are 0.3–2 GB per day for the busiest markets — stream to disk, support Range resumes (curl -C -). States before the bytes: &status=1 answers {state: "ready" | "pending" | "cold"}; a cold day sits in deep archive (days older than ~1 month) — &restore=1 starts the restore (~12 h), then poll &status=1. Derived flat exports of the same day: &format=trades.csv.gz | bbo.csv.gz | liquidations.csv.gz | trades.parquet | liquidations.parquet — exports are prepared server-side first: the first request answers 202 + Retry-After ({state:"pending", reason:"converting"}) while the file is built (minutes for a flagship day), later requests serve the finished file with Content-Length and Range support; &format=…&probe=1 reports ready | pending | empty | failed without bytes.
Day bundles (kind: series_day): the same route with &kind=series_day plus &zip=1 (every contract file of that day as ONE archive — never one request per contract, a day can hold thousands), &manifest=1 (member list), &status=1 (aggregated {ready,pending,cold}), &restore=1, &zip=1&probe=1 (readiness; poll it until state:"ready" — the probe copies the day's files onto our server, and zip=1 answers 202 pending until every file is local; {state:"too_large"} for days over 4 GB), &complete=1 (409 instead of a partial archive). Whole histories: GET https://cryptostruct.com/api/download/order/{order_id}/{bundle_id}/range?kind=series_day&from=YYYY-MM-DD&to=YYYY-MM-DD&t=<token> streams every owned day of the window as one resumable tar (≤ 100 days per stream; &status=1 / &restore=1 / &manifest=1 companions). The full delivery contract is on llms.txt.
Try before buying
GET https://cryptostruct.com/api/samples → curated free full-day files in the exact shop format, each with a direct href (/api/download/sample/{id}/{date}, no auth, ?format= supported). Parser, format reference and recipes: the Agent Skill and the downloads page.
No scripting? Hand the user a link
Buy link (no API call needed): https://cryptostruct.com/shop?i=<instrument_id>&d=<from>..<to> opens the shop with those UTC days of the instrument in the cart; d also takes a comma list or a mix (d=2026-09-01..2026-09-07,2026-09-15). Bundles: https://cryptostruct.com/shop/bundles?bundle=<series_key>&d=…. Only archived days are added (gaps and days still being merged are skipped), at most 366 days of span and 1000 lines per cart; the user pays on the site. Example: https://cryptostruct.com/shop?i=67824&d=2026-09-29..2026-10-05 = seven days of Binance BTCUSDT perpetual ticks. The page lists what landed in the cart.
Free endpoints — no key, no purchase
- Instrument search:
GET https://cryptostruct.com/api/search?q=<text>(section 1) — 100 results per call. - Coverage and days:
GET https://cryptostruct.com/api/instrument/{id}/coverage,…/days,…/calendar?month=YYYY-MM(section 2) — also the sellable day bundles viaGET https://cryptostruct.com/api/bundles. - 1-minute statistics:
GET https://cryptostruct.com/api/analyze/instrument-last-24h-minutes/{id}?format=csv— the rolling last 24 h of any instrument as a 22-column CSV (OHLC, VWAP, trades, turnover, spreads, depth), computed from every recorded trade; the free CSV carries a#source line above the header. - Free sample days:
GET https://cryptostruct.com/api/samples(section 8) — complete UTC days in the shop format, with the CSV/Parquet exports. - Quote:
POST https://cryptostruct.com/api/quote(section 3) — the EUR total and the checkout recipe, keyless.
Instrument ids and master data
Every endpoint keys on the numeric instrument_id — stable, identical across the shop, the MCP server, the analytics pages and the realtime feed. Resolve it with the search API (https://cryptostruct.com/api/search?q=BTCUSDT&venue=binance&class=perp → 67824 for the Binance USDT-M perpetual) and confirm exchange.code + code + type, because the same ticker exists on many venues. Line 1 of every tick file repeats the instrument master data (tick size, lot size, contract specs), so a downloaded day is self-describing; realtime buyers additionally export the master data of every market they hold from the instrument directory (next section).
WebSocket real-time API
Live data is a WebSocket API bought by the UTC day: Binance USDT-M, Binance Spot, Coinbase Spot, Kalshi, Polymarket and OKX stream over WebSocket in JSON or SBE, captured at each venue and delivered at our London, Frankfurt, Ashburn, Ohio or Tokyo endpoints over dedicated lines (Binance and OKX from Tokyo at London, Frankfurt, Ashburn or Ohio; Coinbase from Ashburn at Ohio or Tokyo; Kalshi from Ohio at Tokyo; Polymarket from London at Tokyo) — €49 per instrument-day or €99 per market-day, one API key per account, nothing renews. The port selects the market: Binance USDT-M (port 14004), Binance Spot (port 14003), Coinbase Spot (port 14006), Kalshi (port 14005), Polymarket (port 14007) and OKX (port 14008). The key is created with the first realtime purchase and shown in the account; it authenticates the feed (?apiKey= on the WebSocket URL) and the instrument directory below.
- Real-time crypto market data: every venue, its endpoints and lines with their one-way latency, feed contents per market, prices.
- Connection guide: login, subscribe flow, every message type, keep-alive and reconnect rules, runnable Python and Node examples.
- Feed spec as markdown: the connection guide as one document for coding agents — the API key is read from the environment, never pasted into prompts.
- Instrument directory:
GET https://cryptostruct.com/api/realtime/instruments?market=<code>with the realtime key asAuthorization: Bearer(add&format=csv; poll&since=<as_of>every five minutes for new instruments and state changes) — lists the markets the key holds a day for.
The same flow as MCP tools
Every step above is also an MCP tool: the remote MCP server exposes search, coverage, live statistics, quotes, samples and checkout to any MCP client (keyless free tier; sign-in adds credits, owned-file dedupe and your orders). The MCP docs as markdown and the server card describe it for agents.
Limits and conduct
- Rate limits per IP: search and coverage share the general API bucket;
/api/quote30/min; checkout 10/min; order reads 30/min. Back off on 429. - Never ask the user for card details; never paste a
download_tokeninto a chat transcript; keep the Stripe URL for the user. - Third-party exchange APIs are not proxied here; for a venue's own endpoints and limits, check the current docs of that venue.
Why buy the API here
Four things every page on this site is built on — and the reason the numbers above exist at all.
We record everything
The complete public feed of each venue as it was published: every Level-2 snapshot and update at the venue's full book depth, every trade with its aggressor side, every quote, funding, mark-price and liquidation event — for every instrument the venue lists, every UTC day since we added the venue. Nothing sampled, no top-N cut, no on-demand capture.
Institutional grade
Captured co-located at the venue with the exchange timestamp and our receive timestamp in integer nanoseconds, an event-id chain that makes any gap visible, and one normalized schema across 35+ venues — the same capture our own high-frequency trading engine and enterprise feeds run on.
€1 per instrument-day
Any instrument-day is €1, series-day bundles start at €1 — no subscription, no minimum order, no tiers to unlock. Credit packs lower the effective price and never expire, and every venue has free full-day samples to test against first.
Self-service for everyone
Pick the days in the Data Shop, pay by card as a guest and download immediately — no sales call, no enterprise contract, no KYC. Coding agents buy the same files through the MCP server, and the free Agent Skill teaches them the format.
Agents: /llms.txt · MCP server /mcp · every page as markdown via Accept: text/markdown
Questions about the crypto market data API
Do I need an API key to use the crypto market data API?
Not for the historical flow: search, coverage, quote and the guest checkout are keyless, and the paid order is read and downloaded with the download_token the checkout session returns — no account, no sign-up. The WebSocket real-time feed is the one exception: its key is created with your first realtime purchase and shown in your account.
Is there an OpenAPI specification for the market data API?
Yes — https://cryptostruct.com/openapi.json describes every documented endpoint in OpenAPI 3.1 (also at /.well-known/openapi.json, and linked as service-desc from /.well-known/api-catalog). The walkthrough on this page is the human-readable twin; the markdown rendition at /docs/api.md is the one to hand to a coding agent.
Can I get real-time crypto market data through the API?
Yes — as a WebSocket API bought by the UTC day: Binance USDT-M, Binance Spot, Coinbase Spot, Kalshi, Polymarket and OKX in JSON or SBE, captured at each venue and delivered at our London, Frankfurt, Ashburn, Ohio or Tokyo endpoints over dedicated lines (Binance and OKX from Tokyo at London, Frankfurt, Ashburn or Ohio; Coinbase from Ashburn at Ohio or Tokyo; Kalshi from Ohio at Tokyo; Polymarket from London at Tokyo), €49 per instrument-day or €99 per market-day with one API key, nothing renews. The endpoints, lines and feed contents are on real-time crypto market data, the protocol in the connection guide.
Which endpoints are free to call?
Instrument search, coverage, the day lists and calendars, the sellable-bundle list, the free sample days and the rolling 24-hour 1-minute statistics CSV of any instrument — all keyless, rate-limited per IP. Only the purchased tick days and their CSV/Parquet exports need the order's download_token.
How do I find the instrument id for a symbol?
Call /api/search with the ticker and, ideally, the venue and class (q=BTCUSDT&venue=binance&class=perp): the response carries the numeric instrument_id plus exchange.code, code and type to confirm the market, because the same ticker trades on many venues. The id is stable and identical across the shop, the MCP server and the analytics pages.
What does buying through the API cost?
The same as in the shop: €1 per instrument-day and series-day bundles priced per series, tax-inclusive EUR — no API surcharge, no subscription, no minimum order, no account. The quote returns the exact total before any checkout, and the card payment happens on a Stripe page the user opens.