CUCKOOTRADE

Quickstart

No key, no signup. This works right now, from anywhere:

curl 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=AAPL,CRASH&timeframe=1Day&start=2026-07-01'

Using alpaca-py? Pass url_override="https://cuckootrade.com/api/v1/alpaca" and any non-empty strings as API keys — they're never checked. The API is also self-describing: /api returns a machine-readable index, and /llms.txt summarizes everything for AI coding tools.

API structure & versioning

Every endpoint lives under /api/v1/, and provider-compatible surfaces add the provider's name to the path:

/api/v1/{provider}/<the provider's own path>   ← mimics that provider's wire format
/api/v1/<endpoint>                            ← CuckooTrade-native (e.g. /api/v1/stream)

Everything through the provider segment is CuckooTrade's namespace; everything after it replicates the provider exactly, which is why a client only needs its base URL changed. The v2 in /api/v1/alpaca/v2/stocks/bars is Alpaca's own version, not ours. Three providers are live:

providerbase URLstands in for
alpacahttps://cuckootrade.com/api/v1/alpacadata.alpaca.markets
alphavantagehttps://cuckootrade.com/api/v1/alphavantagewww.alphavantage.co
polygonhttps://cuckootrade.com/api/v1/polygonapi.polygon.io

Two things are versioned, on purpose: the path version (v1) covers the API surface — paths, parameters, error shapes — while the generation parameter covers the data itself. A surface change bumps the path; a data change becomes a new generation, and old generations stay queryable (see determinism).

Provider surface · Alpaca

GET /api/v1/alpaca/v2/stocks/bars

Alpaca-compatible historical OHLCV bars. Regular sessions only (09:30–16:00 ET, NYSE calendar — weekends and US market holidays have no bars; half-days trade as full sessions). Only completed bars are served, so any response you receive is permanently reproducible.

parammeaning
symbolsrequired; comma-separated, max 50. Any well-formed symbol works: curated tickers (AAPL, SPY, …) sit at plausible price levels, everything else gets a stable hash-derived personality.
timeframe[1-59]Min, [1-23]Hour, 1Day, 1Week, [1,2,3,4,6,12]Month. Default 1Day. Coarser bars aggregate exactly from finer ones.
start / endRFC-3339 or YYYY-MM-DD, inclusive. Defaults: last 30 days. Weeks/months clipped by the window are dropped, not served partial.
limittotal bars across all symbols, 1–10000 (default 1000). More data ⇒ next_page_token; pass it back as page_token.
sortasc (default) or desc.
seedcuckoo extension. Any string; returns a different but equally deterministic dataset with the same statistical character. Omit it for the shared default dataset.
generationcuckoo extension. Pins the generator version (currently 1). Responses carry X-Cuckoo-Generation.
adjustment, feed, asof, currencyaccepted and ignored, for Alpaca client compatibility. No corporate actions exist in generation 1.

Also available: /api/v1/alpaca/v2/stocks/{symbol}/bars (single symbol) and /api/v1/alpaca/v2/stocks/bars/latest?symbols=… (last completed bar). Errors are Alpaca-shaped {"code", "message"}, and every message states the valid grammar plus a working example.

Provider surface · Alpha Vantage

GET /api/v1/alphavantage/query

Alpha Vantage's single-endpoint format, faithfully — including its quirks: values are strings, series are keyed newest-first, and errors come back as HTTP 200 with an "Error Message" body, because that is how the real API reports them. apikey accepts anything, including nothing. Intraday is a premium endpoint on Alpha Vantage's free tier; here it's free like everything else.

curl 'https://cuckootrade.com/api/v1/alphavantage/query?function=TIME_SERIES_DAILY&symbol=IBM'
parammeaning
functionrequired: TIME_SERIES_INTRADAY, TIME_SERIES_DAILY, TIME_SERIES_WEEKLY, TIME_SERIES_MONTHLY, or GLOBAL_QUOTE.
symbolrequired; one symbol. Scenario tickers work: symbol=CRASH.
intervalintraday only, required there: 1min, 5min, 15min, 30min, 60min. Labels are interval-end times in US/Eastern.
outputsizecompact (default, last 100 points) or full (~20 years daily; trailing 30 days intraday).
monthintraday only: YYYY-MM serves that calendar month.
apikey, adjusted, extended_hoursaccepted and ignored.
seed / generationcuckoo extensions, same semantics as everywhere else.

Two deliberate deviations from the real thing: only completed bars are served (no partial current day/week/month row — determinism requires it), and sessions are regular-trading-hours only.

Provider surface · Polygon

GET /api/v1/polygon/v2/aggs/ticker/{ticker}/range/…

Polygon's aggregates format: the same envelope (ticker, queryCount, resultsCount, adjusted, results, status, request_id, count), bars as {v, vw, o, c, h, l, t, n} with t in Unix milliseconds, next_url cursor pagination, and {"status": "ERROR"} error bodies. apiKey is accepted and ignored, and status is always OK — synthetic data is never delayed.

curl 'https://cuckootrade.com/api/v1/polygon/v2/aggs/ticker/MSFT/range/1/day/2026-07-01/2026-08-01'
path / parammeaning
range/{multiplier}/{timespan}minute (1–59), hour (1–23), day, week, month (1, 2, 3, 4, 6, 12), quarter (1, 2, 4), year.
{from} / {to}YYYY-MM-DD (inclusive dates) or Unix millisecond timestamps.
sortasc (default) or desc.
limit1–50000 (default 5000); past it, follow next_url.
adjusted, apiKeyaccepted and ignored — no corporate actions exist, so adjusted and unadjusted are the same numbers.
seed / generationcuckoo extensions; carried through next_url automatically.

Also available: /api/v1/polygon/v2/aggs/ticker/{ticker}/prev — the previous session's daily bar, with Polygon's extra "T" field. request_id is a hash of the request rather than a random id, so identical requests stay byte-identical.

The determinism guarantee

Every bar is a pure function of (symbol, timestamp, generation, seed). Identical requests return identical bytes, forever, within a generation — there is no database and no randomness at request time, which is also why fully-specified historical responses ship Cache-Control: immutable. If the generator ever improves, that becomes generation 2; generation 1 stays queryable, so committed fixtures never break.

Scenario tickers

Reserved symbols with scripted, calendar-anchored behavior. Every pattern appears within any 30-day window, so a demo or test never catches a quiet stretch. Responses stay schema-valid — the stress is in the values, never in malformed fields.

tickerbehavior
CRASHsharp ~25% crash mid-month, slow grind recovery
MOONparabolic run-up peaking late in the month, hard correction
FLATzero-range bars pinned at $100.00 — breaks naive autoscaling
GAPPY±5–15% overnight gaps most days, quiet sessions
HALTSminute bars absent during intraday halt windows
STALEfeed freezes mid-session: price repeats, volume is zero, timestamps keep advancing
SPIKEYsingle-minute wicks that spike and instantly revert
PENNY~$0.30 prices, four decimals, high volatility
CHOPPYhigh volatility, zero net drift
SPLITS2:1 forward split monthly — prior closes halve once it goes ex (see restatement)
DIVVYmonthly dividend whose ~1.5% adjustment lands five sessions late
REVISEDa bad print that stays in history until the exchange busts the trade
CuckooTrade native

GET /api/v1/stream (SSE)

Server-sent events — curl-able, no WebSocket library needed. It's what drives the live ticker at the top of every page here. Two clocks: clock=demo (default) is an always-open synthetic session whose price is a pure function of wall time, so every viewer sees the same tick at the same instant; clock=real follows the NYSE calendar and emits completed 1-minute bars, staying silent (heartbeats only) while the market is closed. Max 10 symbols and 5 concurrent streams per address; streams close after 15 minutes — reconnect freely.

curl -N 'https://cuckootrade.com/api/v1/stream?symbols=CUCKOO,CRASH'
CuckooTrade native

as_of — restatement

Real feeds rewrite history. A split or a late dividend adjustment restates bars you already stored, so the same request today and next month does not hand back the same bytes. If you keep bars in a database, that is the case your reconciliation job exists to catch — and the one nothing else will let you rehearse.

as_of models it without giving up determinism. It is a second axis, not a loophole: pin as_of and the bytes are frozen forever, exactly as the determinism guarantee promises. Omit it — the default — and you get what the feed would say today, which for a restating ticker is not what it said last month. Because the schedule is calendar-anchored, you can test a restatement by moving as_of across the date instead of waiting for one:

# before the split processes
curl 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=SPLITS&timeframe=1Day&start=2026-06-01&end=2026-06-30&as_of=2026-07-09'

# after — same window, same request, every close halved
curl 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=SPLITS&timeframe=1Day&start=2026-06-01&end=2026-06-30&as_of=2026-07-13'

# and the ledger that says why
curl 'https://cuckootrade.com/api/v1/corporate-actions?symbols=SPLITS,DIVVY'
parammeaning
as_ofanswer as the feed would have on this date (RFC-3339). Defaults to now. Not Alpaca's asof, which is a symbol-mapping date and is still accepted and ignored.
adjustmentraw (as-traded, never restated), split, dividend, or all. Defaults to all — Alpaca defaults to raw, and this is the one place we deviate, because it is observable only on the three tickers above.

GET /api/v1/corporate-actions?symbols=… lists every action with its announce_date, ex_date and process_date. The gap between the last two is the point of DIVVY: its adjustment lands five sessions after the ex-date, well after a job that polls on the ex-date has decided the month is settled. Requests for these tickers without an explicit as_of are never marked immutably cacheable, for the obvious reason.

Not seeing a change? Every bar response carries X-Cuckoo-As-Of and X-Cuckoo-Restated — read them with curl -i. 2 actions applied (SPLITS split ex 2026-07-10; …) means it worked; 0 actions applied means nothing rewrote these bars. The usual cause is a window sitting after every ex-date, because an action only rewrites bars dated before it — query June and move as_of across July, not the other way round. Note also that omitting start/end defaults to the last 30 days, which is almost always the wrong window for this.

Scope, stated plainly: this models the restatement, not the ex-date price discontinuity — the split rewrites the history in front of it, but you won't see the price halve on the ex-date itself. Corporate actions older than six months count as already baked into history.

CuckooTrade native

scenario= — fault injection

The scenario tickers break the data. This breaks the transport: sockets that die mid-frame, bodies that arrive half-written, requests that fail twice before they work. Add scenario= to any request. Nothing here ever fires unless you ask for it — and because every fault is deterministic, a test that passes once passes every time, which is the whole reason these belong in CI rather than in a chaos dashboard.

effectwherewhat happens
flap:Nbarsfails N times, then succeeds — the one that tests your retry logic recovers
status:CODEbarsreturns that status, in the error shape of whichever provider you called
slow:MSbothdelays the response, or each frame
truncatebothfull Content-Length, half a body; on the stream, one frame cut mid-JSON while the connection lives on
drop:Sstreamcloses the socket at S seconds, mid-frame, with no close event
garbage:NstreamN unparseable frames mixed in with the good ones
silent:Sstreamno data and no heartbeats for S seconds — finds read timeouts
# fails twice, succeeds on the third attempt
curl 'https://cuckootrade.com/api/v1/alpaca/v2/stocks/bars?symbols=AAPL&timeframe=1Day&scenario=flap:2'

# the socket dies twenty seconds in
curl -N 'https://cuckootrade.com/api/v1/stream?symbols=CUCKOO&scenario=drop:20s'

Faulted responses are always Cache-Control: no-store and echo X-Cuckoo-Scenario. One caveat worth knowing: flap has to count attempts, and that counter lives per pod — across our replicas a flap:2 can burn up to four failures before it clears. If you need the count to be exact, run the container yourself (see self-hosting).

Rate limits

60 requests per minute sustained per address, with burst headroom to 120 — no key required. Responses carry RateLimit-Limit / -Remaining / -Reset headers, and a 429 tells you exactly how long to back off. Limits are enforced per replica, so the effective ceiling may be somewhat higher than advertised.

Self-hosting

The API is stateless and MIT-licensed. If your CI pipeline shouldn't depend on an external service, run your own instance: clone the repo and start it with pip install -r api/requirements.txt && python api/api.py, or pull the prebuilt container image published alongside the repo. Determinism means a local instance serves byte-identical data to cuckootrade.com for the same generation.

All data is synthetic. Every response is generated and marked with X-Cuckoo-Synthetic: true. CuckooTrade exists to exercise code paths — development, CI, demos, teaching. It is not market data, must never inform a real trade, and is not a backtesting tool: a strategy that profits against synthetic data has learned the generator, not the market.