View as markdown
Getting started

Gotchas

Things that have caught real users. Quick read; save yourself a confused hour later.

`BTC` is not bitcoin

Symbols are namespaced by asset class. We don’t auto-resolve.

Passing BTC to a stock endpoint returns the Grayscale Bitcoin Mini Trust ETF (a real US-listed equity with that ticker), not bitcoin spot. If you want the crypto, use X:BTCUSD. Same rule for GOLD (Barrick Gold equity) vs CMD:GOLD (gold spot). See Asset symbols for the full prefix table.

Crypto has no `session_open` / `session_high` / `session_low`

Markets that never close don’t have a session.

For equities those columns reflect today's regular session. For crypto they're unset; use the rolling 24-hour columns instead (change_24h, high_24h, low_24h, etc.). Same goes for corporate-event flags: earnings_this_week and friends always come back as false for crypto.

Edge-triggered flags stay true all session

One firing, sticky until the next reset.

Flags like breakout, gap_up, and golden_cross are edge-triggered: they flip true the moment the condition is met and stay true for the rest of the trading session even if the underlying condition reverses. They reset at the start of the next session. If you're asking “is this happening right now,” use the continuous flavor of the flag (e.g. above_yesterday_high); if you're asking “did this happen today,” the edge flag is what you want.

Numeric literals are fully specified

This is an API; we don’t parse suffixes.

Write 1500000000, not 1.5B. Write 0.05 for 5% on decimal columns like day_change_pct, not 5 — scales vary by column, so check the field description on the schema page.

Cursors are opaque

Don’t parse them, don’t cache them across deploys.

List endpoints return next_cursor. Pass it back on the next request and that's it — as ?cursor= in the query string on GET, or in the JSON body on POST. The token bakes in the sort key, page size, and original filters; changing any parameter mid-walk invalidates it. See Pagination for the full mechanics.

Daily-only signals can’t be pulled at 1m or 1h

SMAs, RSI, MACD, fundamentals: daily snapshots only.

Asking /v2/signals/rsi_14/AAPL/history/1m returns signal_not_available_at_interval. Request 1d for any slow-moving signal. The schema page labels each column with the intervals it's stored at; minute-tier columns are price, day_change_pct, gap_pct, volume, relative_volume, day_vwap, and the position-from-extremes columns.

DELETE endpoints return 204 No Content

Empty body on success.

DELETE /v2/webhooks/{id} and DELETE /v2/universes/{id} both return 204 No Content on success. There's no JSON to parse; check the status code. Errors (404) still come back with the standard JSON envelope.

`as_of` means different things on different endpoints

Live time vs requested date.

On live endpoints, as_of is the server time at which the response was assembled (typically within a minute of your call). On historical endpoints (/tickers/{t}/history, /scan?asof=), as_of is the date you asked about. Don't treat an old as_of from a historical call as “stale data” — it's by design.

System universe slugs are reserved

`top_10` and `top_100` you don’t own.

When you fetch /v2/universes/top_10 you get a system universe, even though you didn't create one. You can't create a universe with a slug that collides with a system one (slug_taken on POST). You can't delete or edit a system universe either; both come back 403.

Webhook deliveries can fire after delete

Queued deliveries capture target + secret at queue time.

If a webhook is in the middle of an evaluation cycle when you call DELETE /v2/webhooks/{id}, any deliveries already enqueued can still fire. They carry the original target URL and signing secret. To pause without this race, stop responding 2xx at your target and the subscription will flip to disabled after consecutive failures.