Subscribe to a ticker
https://api.tickerbot.io/ v2/ tickers/ {ticker}/ subscribePush one ticker: we POST your endpoint whenever it matches the condition you give.
Body parameters
stringrequiredCase-insensitive. Equities are bare symbols (AAPL); every other class carries a prefix — rates (R:SOFR), crypto (X:BTCUSD), fx (X:EURUSD). Bare BTC/ETH are US-listed ETFs, not spot crypto. See Tickers.
stringrequiredWHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (condition accepted as an alias.) See the signals catalog for columns + flags you can compose.
stringOriginal name for q — accepted as well. The same WHERE-clause fragment; send either spelling.
stringhttps:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard).
enumDelivery channel. webhook (POST to target_url), discord (post an embed to discord_url), in_app (dashboard only), or mobile_push (notify a phone signed in to the Tickerbot mobile app; requires a device_id from POST /v2/devices/register). Inferred when omitted: webhook if target_url is set, discord if discord_url is set, else in_app. slack is reserved and returns 501. See the Delivery channels guide.
webhook | POST the payload to an HTTPS endpoint you own, signed with X-Tickerbot-Signature. |
discord | Post to a Discord channel via its webhook URL. Delivered as a Discord embed, never HMAC-signed — the URL is itself the credential. |
in_app | Deliver to the in-app feed. No external receiver, so nothing to validate and no test_url. |
mobile_push | Push to a registered device. Requires a device_id from Register a device. |
stringDiscord incoming-webhook URL (https://discord.com/api/webhooks/…). Required when channel is discord. Stored as a posting credential: the create response echoes it back under channel_config, but every later read (list, get, deliveries) strips it and sets channel_config_present: true instead.
stringDevice to notify, from POST /v2/devices/register. Required when channel is mobile_push; unknown ids are a 404 device_not_found.
enumHow often to evaluate. realtime (the default) is evaluated on every data refresh (~1×/min); hourly and nyse_open throttle to a batch schedule. 1m is a deprecated alias for realtime.
realtime | Evaluate on every refresh — the canonical value. (1m is a deprecated alias that collapses to this.) |
hourly | Batch schedule: evaluate once an hour. |
nyse_open | Batch schedule: evaluate once per session, at the NYSE open. |
stringHuman-readable label (up to 80 chars). Defaults to <TICKER>: <query>.
string[]Comma-separated extra signals to include in each fired payload match row, beyond the standard set (ticker, name, asset_type, price, change_1d_pct, market_cap). Each must be a real signal; an unknown signal is rejected at creation. fields accepted as an alias — and note the RESPONSE reports them under fields, as an array.
stringdefault market_capSignal the fired payload's match lists are sorted by before the 100-row cap is applied, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real signal (validated at creation).
enumdefault descSort direction for order.
asc | Ascending — smallest or earliest first. |
descdefault | Descending — largest or most recent first. |
Headers
stringOptional unique string (≤255 chars). A retry carrying the same key within 24h replays the original response (Idempotency-Replayed: true header) instead of creating a duplicate; a concurrent duplicate gets 409 idempotency_in_flight; reusing a key for a *different* request (another method or path) gets 422 idempotency_key_reused. 5xx responses are not stored — those retries re-execute.
Returns
as_ofstringServer time this response was assembled (ISO 8601).
idstringThe webhook id — `wh_…`, the handle for every other call on this record.
namestringYour label for the subscription.
qstringThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
rule_idstringLegacy link to a v1 alert rule; `null` on everything created through v2.
fieldsstringExtra signals carried on each fired match row; `null` means the standard set.
orderstringSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
dirstringSort direction for that list; `null` means the default (`desc`).
universe_idstringUniverse the trigger is scoped to, or `null` for the whole market.
cadencestringHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelstringWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
target_urlstringYour HTTPS endpoint; `null` on every channel except `webhook`.
deliverystringLegacy alias of `channel`, kept aligned for older readers.
statusstring`active` or `disabled`. Auto-disable follows repeated delivery failure.
sourcestringWhich API version created the record; `v2` for anything you create today.
subscription_originobjectWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valuestringThe trigger's value at the last evaluation; `null` until it has run.
created_atnumberCreation timestamp.
updated_atnumberLast modification timestamp.
last_firednumberWhen a delivery last went out; `null` if it never has.
last_match_setarrayTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
last_eval_error_atnumberWhen the last evaluation error happened; `null` on a healthy hook.
last_errorstringThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
next_eval_atnumberWhen the evaluator will next consider this subscription.
last_evaluated_atnumberWhen it was last evaluated; `null` until the first run.
event_kindsarrayEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
event_qstringEvent-trigger webhooks only: the payload filter, or `null`.
event_tickersarrayEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
trigger_kindstringEvent-trigger webhooks only: `event`.
channel_configobjectReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
signing_secretstringReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
test_urlstringReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
_metaobjectReturned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.
Status codes
201GET /v2/webhooks/{id}, plus test_url and, uniquely on create, signing_secret (shown once, for HMAC verification) and the raw channel_config you supplied. Later reads strip both.400404409idempotency_in_flight — a concurrent request carried the same Idempotency-Key.422idempotency_key_reused — the Idempotency-Key was already used for a different request.501channel_not_implemented — channel: "slack" is reserved.Notes
- Shorthand for
POST /v2/webhookswith a{ type: "ticker" }trigger — one of the four doors to the same webhook object, listed, updated, and deleted at/v2/webhooks. It counts against your account-wide webhook cap. - The predicate is scoped to the path ticker automatically, so do not add
ticker = '…'yourself. (condition, the original name, is still accepted.) - Returns the same shape as
GET /v2/webhooks/{id}, plus atest_urlfor wire-delivered channels (webhook,discord). Subscribing does NOT auto-fire anything at your endpoint — hit the test URL to send a real-shapewebhook.firedand validate your receiver. In-app subscriptions have no external receiver and carry notest_url. - Custom signals referenced in the predicate are expanded and frozen into the webhook at creation. Editing the signal later does not change this subscription — re-subscribe to apply.
More examples
curl -X POST "https://api.tickerbot.io/v2/tickers/NVDA/subscribe" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"rsi_oversold AND high_volume_alert","channel":"discord","discord_url":"https://discord.com/api/webhooks/123456789012345678/aBcDeF…","cadence":"realtime"}'{
"as_of": "2026-07-27T16:41:00.000Z",
"id": "wh_dC9rTq-k77p",
"name": "NVDA: rsi_oversold AND high_volume_alert",
"q": "ticker = 'NVDA' AND (rsi_oversold AND high_volume_alert)",
"cadence": "realtime",
"target_url": null,
"channel": "discord",
"delivery": "discord",
"channel_config": {
"url": "https://discord.com/api/webhooks/123456789012345678/aBcDeF…"
},
"signing_secret": "whsec_…",
"status": "active",
"subscription_origin": {
"type": "ticker",
"ref": "NVDA",
"condition": "rsi_oversold AND high_volume_alert"
},
"test_url": "/v2/webhooks/wh_dC9rTq-k77p/test",
"created_at": 1785170460
}