Signal firings
https://api.tickerbot.io/ v2/ events?kind=signalThe moments a built-in boolean flipped, as enter/exit point-events. Opt-in: name signal in kind and it joins the stream.
Query parameters
stringrequiredfixed by this pageInclude signal explicitly — it never joins an unfiltered stream.
stringOne built-in boolean signal. Optional in filter mode; REQUIRED with q or join=state, which is what keeps the query on an index. An unknown name, a numeric signal, or a custom signal is a 400 — never a silent empty list.
enumenter (false→true) or exit (true→false). Optional everywhere — an ordinary filter. Same word /v2/series uses for the same primitive.
integerInterval-union before the unpivot (TB-331, from the retiring spans route): runs of the signal separated by ≤ N seconds collapse into one, so a signal with thousands of per-tick fragments yields one enter and one exit per real run — 3600 for hourly signals, 86400 for daily. Default 0 = raw fragments. Requires signal AND an explicit tickers list (up to 50), filter mode only (no q / join / group_by): runs are computed over the signal's whole history per ticker, then the window and cursor apply to the edges. A cursor pins it; resend it unchanged when paging.
stringSingle symbol. Beats tickers when both are passed.
string[]Comma-separated symbols, up to 50. Not combinable with universe; ticker wins when both are passed.
stringSystem or caller-owned universe slug.
stringYYYY-MM-DD or ISO timestamp (inclusive). since is accepted as an alias.
stringYYYY-MM-DD or ISO timestamp — a bare YYYY-MM-DD means through the end of that day; timestamps are exclusive. until is accepted as an alias.
stringSQL filter. This kind's payload fields are first-class typed columns here: signal, transition (text), price, definition_version (numeric) — plus ticker-state signals with join=state (a state column shadows a same-named payload field there: spell payload->>'price' if you mean the firing price under the join). Requires signal= on this kind (the named signal keeps the query on an index). See the signals catalog for columns + flags you can compose.
enumstate widens q to ticker-state signals evaluated as of each firing's timestamp. Requires signal=.
state | Widen the grammar to ticker-state columns, evaluated as of each event’s timestamp. Resolves at daily grain. |
enumdefault autoGrain the per-event state is reconstructed at when join=state: 1m, 1h, 1d, or auto (default → 1d).
1m | One minute. The finest stored tier; carries the intraday column subset and ~1,100 of ~13,700 tickers. |
1h | One hour. Stored tier, intraday column subset, full universe. |
1d | One day. The full-history, full-column tier — the only grain that carries daily-only columns (SMAs, RSI, fundamentals). |
autodefault | Let the server pick the finest grain whose columns and ticker coverage both work out, resolving upward on a miss. Reported in _meta.interval. |
integerdefault 50Rows per page. Max 1000 — raise it and filter to one signal client-side.
stringOpaque cursor from the previous response.
Returns
as_ofstringServer time this response was assembled (ISO 8601).
queryobjectYour filters, echoed.
countnumberRows in this page.
next_cursorstringOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.
resultsarrayOne row per event, newest first.
tickerstringSymbol that flipped.
tsstringThe edge's timestamp (minute-grained).
kindstringAlways `signal` on this view.
payloadobjectThe flip.
signalstringThe built-in boolean that flipped.
transitionstring`enter` (false→true) or `exit` (true→false). **Renamed from `direction` on 2026-08-05** to match `/v2/series`, which has always called this a transition.
pricenumberoptionalPrice at the edge.
definition_versionnumberoptionalDefinition version of the boolean that produced the edge — an integer (`6` is current-canonical; `1`/`3`/`4`/`5` are legacy).
Status codes
200400not_supported_for_kind — q or join=state on kind=signal without signal=, or group_by on kind=signal at all. Also not_a_flag_signal / not_supported_for_custom_signal when signal= names a numeric signal or a custom signal.503kind_unavailable — the signal event log is temporarily unavailable; retry later. Other kinds are unaffected. Validation runs first, so a 503 means the query itself is well-formed — a request that would 400 still 400s while the log is down.Notes
- This is
GET /v2/eventswithkind=signal; it has its own page because the payload is its own contract. Firings are computed by the same shared rule that fires webhooks, so the log and the push cannot disagree. Latency ≈ 1 minute (written by the minute pipeline). - Name the signal to use the grammar. Filter mode needs nothing extra — scope with
kind/ticker/tickers/universe/from/to/limitand page.qandjoin=stateadditionally requiresignal=<name>: the firing log is ~175M rows, and naming the signal is what lets it be read from an index instead of scanned, so everygolden_crossenter across the whole market for a week comes back in milliseconds. Without it the request is refused with a 400 naming the param rather than timing out. group_byis not available on this kind. An aggregate has nolimitto stop at, so it reads every firing in the window and the cost tracks how often the signal fires; naming asignal=does not rescue it. A mixedkindlist containingsignalis refused for the same reason — dropsignalfromkindto aggregate the rest.- One fact, two shapes: a flip as a point-event is this kind; flips inline on a value series is
transitions_onlyon State over time. The on/off-interval shape, Signal spans, is deprecated (sunset 2026-10-31);merge_gap_secondshere yields oneenterand oneexitper run instead. - Built-in booleans only — a forward-only boolean has no firings before its first computed day (see coverage). Numeric-threshold flips are an as-of question, not a stored event.
- For pushes on flips, use a signal-trigger webhook — signal webhooks ride the sub-minute state path, not event ingest.
More examples
curl "https://api.tickerbot.io/v2/events?kind=signal&signal=golden_cross&transition=enter&from=2026-08-17&limit=50" \
-H "Authorization: Bearer YOUR_KEY"{
"as_of": "2026-08-12T15:07:53.228Z",
"query": { "kind": "signal", "signal": "golden_cross", "transition": "enter",
"since": "2026-07-01T00:00:00.000Z", "limit": 50 },
"count": 2,
"next_cursor": "eyJ2IjoxLCJwb3MiOnsic2ln…",
"results": [
{ "ticker": "NVDA", "ts": "2026-07-22T13:31:00.000Z", "kind": "signal",
"payload": { "signal": "golden_cross", "transition": "enter",
"price": 181.4, "definition_version": 1 } },
{ "ticker": "KO", "ts": "2026-07-18T14:02:00.000Z", "kind": "signal",
"payload": { "signal": "golden_cross", "transition": "enter",
"price": 69.12, "definition_version": 1 } }
]
}curl -G "https://api.tickerbot.io/v2/events" \
--data-urlencode "kind=signal" \
--data-urlencode "signal=gap_up" \
--data-urlencode "transition=enter" \
--data-urlencode "join=state" \
--data-urlencode "q=above_sma_200 = true" \
--data-urlencode "from=2026-08-17" \
-H "Authorization: Bearer YOUR_KEY"{
"as_of": "2026-08-12T15:07:53.228Z",
"query": { "kind": "signal", "signal": "gap_up", "transition": "enter",
"q": "above_sma_200 = true", "join": "state", "state_resolution": "1d" },
"_meta": { "state_interval": "1d" },
"count": 1,
"next_cursor": null,
"results": [
{ "ticker": "MSFT", "ts": "2026-07-24T13:30:00.000Z", "kind": "signal",
"payload": { "signal": "gap_up", "transition": "enter",
"price": 511.7, "definition_version": 1 } }
]
}