← tickerbot.io
View as markdown

Signal firings

GEThttps://api.tickerbot.io/v2/events?kind=signal

The moments a built-in boolean flipped, as enter/exit point-events. Opt-in: name signal in kind and it joins the stream.

stringrequiredfixed by this page

Include signal explicitly — it never joins an unfiltered stream.

string

One 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.

enum

enter (false→true) or exit (true→false). Optional everywhere — an ordinary filter. Same word /v2/series uses for the same primitive.

integer

Interval-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.

string

Single symbol. Beats tickers when both are passed.

string[]

Comma-separated symbols, up to 50. Not combinable with universe; ticker wins when both are passed.

string

System or caller-owned universe slug.

string

YYYY-MM-DD or ISO timestamp (inclusive). since is accepted as an alias.

string

YYYY-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.

string

SQL 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.

enum

state widens q to ticker-state signals evaluated as of each firing's timestamp. Requires signal=.

stateWiden the grammar to ticker-state columns, evaluated as of each event’s timestamp. Resolves at daily grain.
enumdefault auto

Grain the per-event state is reconstructed at when join=state: 1m, 1h, 1d, or auto (default → 1d).

1mOne minute. The finest stored tier; carries the intraday column subset and ~1,100 of ~13,700 tickers.
1hOne hour. Stored tier, intraday column subset, full universe.
1dOne day. The full-history, full-column tier — the only grain that carries daily-only columns (SMAs, RSI, fundamentals).
autodefaultLet the server pick the finest grain whose columns and ticker coverage both work out, resolving upward on a miss. Reported in _meta.interval.
integerdefault 50

Rows per page. Max 1000 — raise it and filter to one signal client-side.

string

Opaque cursor from the previous response.

as_ofstring

Server time this response was assembled (ISO 8601).

queryobject

Your filters, echoed.

countnumber

Rows in this page.

next_cursorstring

Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.

resultsarray

One row per event, newest first.

tickerstring

Symbol that flipped.

tsstring

The edge's timestamp (minute-grained).

kindstring

Always `signal` on this view.

payloadobject

The flip.

signalstring

The 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.

pricenumberoptional

Price at the edge.

definition_versionnumberoptional

Definition version of the boolean that produced the edge — an integer (`6` is current-canonical; `1`/`3`/`4`/`5` are legacy).

200
Standard events envelope.
400
not_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.
503
kind_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.
  • This is GET /v2/events with kind=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 / limit and page. q and join=state additionally require signal=<name>: the firing log is ~175M rows, and naming the signal is what lets it be read from an index instead of scanned, so every golden_cross enter 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_by is not available on this kind. An aggregate has no limit to stop at, so it reads every firing in the window and the cost tracks how often the signal fires; naming a signal= does not rescue it. A mixed kind list containing signal is refused for the same reason — drop signal from kind to aggregate the rest.
  • One fact, two shapes: a flip as a point-event is this kind; flips inline on a value series is transitions_only on State over time. The on/off-interval shape, Signal spans, is deprecated (sunset 2026-10-31); merge_gap_seconds here yields one enter and one exit per 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.
One signal, across the whole market
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"
Response
{
  "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 } }
  ]
}
Cross-reference against state at the moment it fired
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"
Response
{
  "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 } }
  ]
}