View as markdown
Endpoints · Scan

Subscribe to a scan query

POSThttps://api.tickerbot.io/v2/scan/subscribe

Push a whole query: we POST your endpoint every time the match set changes.

stringrequired

WHERE-clause expression using signal/column names — the same grammar and the same 4000-char cap as POST /v2/scan, so anything scannable is subscribable. Custom signals are expanded and frozen in at creation. See the schema for columns + flags you can compose.

string

System or user-owned universe to scope the scan. universe_id accepted as an alias. Unknown universes are a 404 universe_not_found.

string

https:// URL to POST when the match set changes. Omit for in-app delivery.

enum

Delivery channel. webhook (POST to target_url), discord (embed to discord_url), in_app (dashboard only), or mobile_push (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.

webhookPOST the payload to an HTTPS endpoint you own, signed with X-Tickerbot-Signature.
discordPost to a Discord channel via its webhook URL. Delivered as a Discord embed, never HMAC-signed — the URL is itself the credential.
in_appDeliver to the in-app feed. No external receiver, so nothing to validate and no test_url.
mobile_pushPush to a registered device. Requires a device_id from Register a device.
string

Discord incoming-webhook URL. 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.

string

Device to notify, from POST /v2/devices/register. Required when channel is mobile_push; unknown ids are a 404 device_not_found.

enum

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.

realtimeEvaluate on every refresh — the canonical value. (1m is a deprecated alias that collapses to this.)
hourlyBatch schedule: evaluate once an hour.
nyse_openBatch schedule: evaluate once per session, at the NYSE open.
string

Human-readable label (up to 80 chars). Defaults to scan: <q>.

string[]

Extra columns per fired payload match row, beyond the standard set (ticker, name, asset_type, price, day_change_pct, market_cap). Each must be a real column; unknown ones are rejected at creation. fields accepted as an alias — and the RESPONSE reports them under fields, as an array.

stringdefault market_cap

Column the payload's match lists are sorted by before the 100-row cap applies, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real column (validated at creation).

enumdefault desc

Sort direction for order.

ascAscending — smallest or earliest first.
descdefaultDescending — largest or most recent first.
string

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

201
Webhook subscription created — the same shape as GET /v2/webhooks/{id}, plus test_url (wire-delivered channels only) and, uniquely on create, signing_secret (shown once, for HMAC verification) and the raw channel_config you supplied. Later reads strip both.
400
bad_request or invalid_query — missing/malformed q, or invalid target_url/cadence.
404
universe_not_found — unknown universe; device_not_found — unknown device_id.
409
idempotency_in_flight — a concurrent request carried the same Idempotency-Key.
422
idempotency_key_reused — the Idempotency-Key was already used for a different request.
501
channel_not_implementedchannel: "slack" is reserved.
  • Shorthand for POST /v2/webhooks with a { type: "scan" } trigger — one of the four doors to the same webhook object, listed, updated, and deleted at /v2/webhooks. Optionally scope it to a universe.
  • Subscribes to a free-form q, the same grammar as POST /v2/scan, and fires whenever tickers enter or leave the match set. q takes the same 4,000 characters scan does, checked before custom-signal inlining — so any q the scan endpoint accepts is subscribable.
  • Payload row lists (matches, current_matches) are capped at 100 entries, while matched_count / current_match_count always carry the true totals and a matches_truncated / current_matches_truncated flag appears when a list was capped. A capped list is the top 100 by order (default market_cap descending), and the payload echoes the order/dir used — so the 100 you receive are deterministic and stable between fires, not an arbitrary sample.
  • Wire-delivered subscriptions (webhook, discord) come back with a test_url. Subscribing does NOT auto-fire — call it to send a real-shape webhook.fired and validate your receiver. In-app subscriptions have no external receiver and carry no test_url.
  • Custom signals referenced in q are expanded and frozen into the webhook at creation. Editing the signal later does not change this subscription — re-subscribe to apply.