- ChangedWebhook cadence is no longer plan-gated — every plan (including Free, when it has webhooks) can choose any cadence. The `cadence_above_plan_max` (403) error is retired; it can no longer occur.
- ChangedThe real-time cadence is now `realtime` (the default). It is evaluated on every data refresh (~1×/min), so a match is delivered within seconds of the snapshot that first contains it — no per-minute batching. `1m` is a deprecated alias for `realtime`: it is still accepted on create/update and is normalized to `realtime`, which is what responses now return.
Getting started
Changelog
Notable changes to the API. Newest first. Backward-incompatible changes ship as a new path version; everything within a major version is additive after launch.
- ChangedPlans restructured around a new $0 Free tier, now the default on signup. All data is available on every plan, including Free: full universe, every signal, real-time data, history, `asof`, news, and analyst archives. Paid plans differ by rate limit, webhook count, and custom signal count. The 14-day trial is retired; cancelling returns an account to Free rather than revoking access.
- AddedRules are now validated at creation. Every subscribe endpoint dry-runs your query and rejects an unrunnable rule (unknown column, bad syntax) with `400 invalid_query` and the underlying message.
- AddedOptional `fields` on `/v2/tickers/{t}/subscribe`, `/v2/signals/{s}/subscribe`, and `/v2/scan/subscribe` — a comma-separated list of extra columns to include in each fired payload's match rows, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `day_change_pct`, `market_cap`). Mirrors `fields` on `/v2/scan`; each must be a real column or the subscription is rejected at creation.
- Added`GET /v2/analyst/events` — per-event analyst rating history from a ~14-year archive: upgrades, downgrades, initiations, and price-target changes, each with the firm, analyst, new and previous rating, and new and previous price target. Filter by `ticker`/`tickers` (≤50), `since`/`until`, `firm` (case-insensitive), and `action`; results are newest-first and page with `cursor`. At least one filter is required. Scale and above — the live `last_rating_*` columns on `/v2/tickers` and the `recent_*` analyst flags on `/v2/scan` remain available on every plan. See /docs/endpoints/analyst/events.
- Added`tickerbot_list_analyst_events` MCP tool wraps the endpoint above, so agents can pull analyst rating history in chat.
- AddedWebhook delivery channels. Every subscribe endpoint (`/v2/tickers/{t}/subscribe`, `/v2/signals/{s}/subscribe`, `/v2/scan/subscribe`) now takes a `channel` discriminator — `webhook` (signed POST to `target_url`), `discord` (an embed posted to a Discord channel), or `in_app` (dashboard-only). Omit it and we infer: `webhook` when `target_url` is set, `discord` when `discord_url` is set, otherwise `in_app`. See the Delivery channels guide at /docs/endpoints/webhooks/channels.
- AddedDiscord delivery. Pass `channel: "discord"` plus a `discord_url` (a Discord incoming-webhook URL) and each fire posts a formatted embed — subscription name, composed query, and the first dozen matching tickers with price and day change. No HMAC: the URL is the credential, so it is stored masked and stripped from list/get responses, which expose `channel_config_present: true` instead. `429`s honor Discord's `Retry-After`; other `4xx`s are permanent failures; `5xx`/network errors ride the standard retry ladder.
- Changed`POST /v2/webhooks/{id}/test` is channel-aware — for a Discord subscription it posts a real-shape test embed (titled with a `[TEST]` prefix) and returns the inline outcome, same as the HTTPS test fire.
- ChangedThe delivery channel is fixed at create time. `PATCH /v2/webhooks/{id}` still toggles a `webhook` subscription between POST-to-URL and in-app, but cannot switch to or from Discord — delete and re-subscribe to change channel.
- ChangedSubscribing no longer auto-fires a `webhook.ping` at your endpoint. One delivery shape — `webhook.fired` — so handler code only needs to handle one event, and the first payload your endpoint ever receives is identical to every future one. New webhooks ship in `status: "active"` immediately.
- Added`POST /v2/webhooks/{id}/test` fires a real-shape `webhook.fired` POST to your `target_url` synchronously and returns the inline HTTP outcome (`delivered`, `http_status`, `elapsed_ms`, `error`). Body is byte-identical to a real fire — same fields, same HMAC signature — with an `X-Tickerbot-Test: true` header as the only marker. Test fires are one-shot: failures are recorded as `permanent_failure` and never enter the retry queue or trigger auto-disable on the parent webhook.
- Added`test_url` field on subscribe responses points at the test endpoint above. The dashboard also surfaces a "Send test fire" button on the post-create modal and on the webhook detail page.
- Removed`webhook.ping` event type. The `pending_verification` status is no longer emitted on new webhooks.
- Added`GET /v2/tickers/{ticker}/bars/{interval}` — OHLCV bars at `1s`, `1m`, `5m`, `15m`, `30m`, `1h`, `1d`. Single symbol or comma-separated bulk (response keyed by symbol). Paging via `before` + `limit` (with `cursor`/`next_cursor` as sugar); `asof` returns a single point-in-time bar. `1d`/`1h` cover the full universe with full history; sub-hour covers the active universe and back-fills from the provider on first request. Available on every plan, all intervals.
- Added`1s` (1-second) interval on the bars endpoint, served on demand: from our store when present, otherwise passed through from the provider in the same call. Second data is not bulk-backfilled — it accumulates as requested. Best paired with a tight `before`+`limit` window.
- Added`POST /v2/tickers/{ticker}/subscribe`, `POST /v2/signals/{signal}/subscribe`, and `POST /v2/scan/subscribe` — subscribe to a webhook directly from the resource you want to watch. The new endpoints all return the same webhook record shape; `/v2/webhooks` becomes a pure registry for listing, inspecting, deleting, and re-enabling subscriptions.
- Removed`POST /v2/webhooks` — replaced by the resource-level subscribe endpoints above. Existing webhooks created via the old path keep firing unchanged.
- Removed`/v2/rules` — saved-bundle layer retired. Inline the `q` directly on `/v2/scan` or on a subscribe endpoint; reference a universe via `?universe=<slug>` instead of bundling it.
- Added`?asof=` is now uniform across every read: `/v2/tickers/{ticker}?asof=`, `/v2/signals/{signal}?asof=`, `/v2/scan?asof=`. Returns the universe as it stood at the close of the requested day, sourced from `signal_daily_state`. `_meta.frozen_fields` lists columns that aren't historized (sector, industry, exchange, asset_type).
- ChangedEvery paid plan now ships replayable history on every read, unlimited universes, and webhooks scaling by tier (Hobby 10 · Pro 100 · Scale unlimited). The Pro → Scale upgrade is content (news archive, custom signals).
- ChangedNews archive (`/v2/news/scan`) moved to Scale and above (previously Pro). The live `news_volume` and `news_volume_weighted_sentiment` columns on `/v2/tickers` and `/v2/scan` remain available on every paid plan.
- ChangedCustom signals (`POST/PATCH/DELETE /v2/signals/{name}`) moved to Scale and above. The unified `/v2/signals` catalog remains readable on every plan.
- ChangedWebhook visibility is account-scoped: every API key on the same account sees the same webhook registry. Rotating a key no longer strands subscriptions.
- ChangedFree trial no longer requires a credit card. New signups are placed on Hobby for 14 days; an API key is minted from the dashboard checklist with no Stripe interaction. Add a payment method anytime to keep going past the trial.
- Changed`/onboarding` retired. The dashboard is now the unified post-signup landing — trial countdown, account-status banner, and onboarding checklist all live there. Stripe Checkout success and cancel URLs now point back to `/dashboard` and `/dashboard/billing`.
- Added`402 trial_ended` error code (was `subscription_required`) returned by `/v2/*` when a no-card trial has expired. Body message links to `/dashboard/billing` to add a payment method.
- AddedCustom (expression) signals — name a SQL `WHERE`-clause expression once, then reference it as a bare identifier in any predicate context (scan `q`, another custom signal's `expr`, webhook subscribe body). Compiles + inlines at create time; recursion is detected and capped at depth 5. CRUD: `POST /v2/signals`, `GET /v2/signals` (unified catalog of built-ins + your customs), `PATCH/DELETE /v2/signals/{name}`. Cascade-safe delete refuses with 409 `signal_referenced` (carries `referencing_signals` array) — pass `?force=true` to override.
- Added`GET /v2/signals/{name}/{ticker}/events` — occurrence records (start/end timestamps + start/end prices) for a single signal on a single ticker. State-style signals return windowed regions; event-style signals return point events. Drives the dashboard chart's flag-overlay rail.
- ChangedHobby now includes full per-ticker history and historical scans (previously live-data only). Rate limit on Hobby raised from 60 to 600 req/min.
- ChangedThe demo ticker (AAPL) now bypasses the plan top-N scope filter on every plan, so the public docs and the per-signal reference page render without auth on every tier.
- Changed`GET/POST /v2/news/scan` replaces the original `/v2/news` + `/v2/news/{id}` pair. One SQL-style endpoint, two shapes — article rows by default, aggregate rollups when `group_by` is supplied. Auto-joins `UNNEST(tickers) AS tk` whenever any clause references the `tk` alias. "As-of" is just a WHERE filter on `time_published`; no separate parameter.
- ChangedNews archive access now requires Pro or above (was Hobby). The two live news columns on the ticker table (`news_volume`, `news_volume_weighted_sentiment`) remain available on every paid plan via `/v2/scan` and `/v2/tickers`.
- Added`/v2/sandbox/news/scan` — unauthenticated, IP-rate-limited mirror of the scan endpoint, capped at 50 rows.
- Added`GET /v2/news` — paginated, filterable news feed. Articles indexed back to 2015, refreshed every 15 minutes. Filterable by `tickers`, `topics`, time range, `min_relevance`, `min_sentiment`/`max_sentiment`. Hobby and above; per-call cap 50/200/1000 by plan. (Superseded the next day by `/v2/news/scan`.)
- Added`GET /v2/news/{id}` — fetch a single article by id. (Superseded the next day by `/v2/news/scan`.)
- AddedTwo new fields on the ticker schema: `news_volume` (24-hour rolling count of mentions at relevance ≥ 0.6) and `news_volume_weighted_sentiment` (relevance-weighted average sentiment, attenuated by tanh(volume/15) so quiet tickers read near zero). Both refresh every 15 minutes. Queryable through `/v2/scan` and `/v2/tickers`.
- RemovedThe four previously-stale news columns on `ticker` (`latest_news_at`, `recent_news_count`, `highly_relevant_news_count`, `news_sentiment_volatility`) have been removed. They were never reliably populated; the new two-field design above supersedes them.
- RemovedFree tier removed. Every plan (Hobby, Pro, Scale) now starts with a 14-day free trial; cancel anytime.
- Added`/v2/universes` — create and manage named ticker lists. System universes `top_10` and `top_100` (rebalanced monthly by dollar volume) are available to every account.
- Added`/v2/rules` — save a `{q, universe_id?, order, dir, fields}` bundle and reference it from `/v2/scan?rule=` or `/v2/webhooks`.
- Added`?universe=` parameter on `/v2/scan`, `/v2/signals/{signal}`, and `/v2/tickers` — scope queries to a system or owned universe.
- Added`?asof=` on `/v2/scan` — folds the historical scan into the main scan endpoint. `/v2/scan/history` remains as a backwards-compatible alias.
- AddedWebhook `cadence` field — pick `1m` (real-time, the default), `hourly`, or `nyse_open`.
- AddedWebhook `rule_id` body field — subscribe to a saved rule instead of inlining `q`.
- ChangedFree and Hobby plans now auto-scope ticker queries to the plan top-N (10 / 100). Response carries `_meta.scope` describing what got applied.
- ChangedNew pricing matrix: ticker scope, webhook cadence, max universes/rules/webhooks. See /pricing for the full grid.
- ChangedAPI base path moves from `/v1` to `/v2`. v1 keeps working at its existing path for now; new integrations should use v2.
- Added`/v2/signals/{signal}` lists tickers where a signal matches. Numeric signals take a `?condition=` (e.g. `>70`); boolean flags are auto-detected.
- Added`/v2/signals/{signal}/{ticker}/history/{interval}` returns per-signal time-series at the requested resolution (`1d` / `1h` / `1m`). Daily history covers all-time; minute history is two years for top-200 tickers.
- Added`/v2/scan/history?asof=` snapshots the universe scan at any past date, as-of-that-day. Falls under the same SQL grammar as live `/v2/scan`.
- Added`/v2/tickers/{ticker}/history?asof=` returns the wide ticker row as it stood on a past date, with quarterly fundamentals (assets, equity, EPS…) joined in by filing date.
- Added`/v2/tickers/{ticker}/events` returns a unified event log: splits, dividends, analyst rating changes, all sorted newest-first with cursor pagination.
- Changedv2 uses scanner-native column names everywhere — the legacy v1 docs vocabulary (`day_change_pct`, `asset_type`) is gone in favor of `day_change_pc`, `type`. The full set is documented on the schema page.
- ChangedWebhooks now live under `/v2/webhooks` with the same shape as `/v1/webhooks`. Existing v1 webhooks keep firing unchanged.
- AddedFree tier: live API key with no card on file. 10 req/min, 7 days of signal history, all query endpoints; no webhooks.
- AddedScale tier ($199/mo): 1,000 req/min, 1-year history, up to 100 webhook subscriptions, Slack Connect support.
- ChangedPricing: Pro replaces Core at $79/mo (down from $99), 500 req/min, 6-month history. Hobby now includes up to 3 webhook subscriptions.
- Added`/v1/signals/{ticker}/{signal}` enforces per-plan history depth. Requests with `from` older than the plan ceiling return `400 history_window_exceeded` with `max_history_days` + `earliest_allowed_from` in the response body.
- ChangedSubscription cancellation falls back to Free instead of revoking API keys — your integration keeps working at Free-tier limits. Webhooks over the new tier limit are auto-disabled (config preserved); resubscribe to re-enable.
- Fixed`GET /v1/signals/{ticker}/{signal}` route now registered (previously returned 404 despite being documented).
- AddedInitial public release of the Tickerbot API. Four endpoints under /v1: tickers, signals, scan, webhooks.
- Added`GET /v1/tickers/{ticker}` and bulk `GET /v1/tickers?tickers=...` return the full curated ticker object.
- Added`GET /v1/signals/{ticker}/{signal}` returns time-series history: numeric bars, continuous-flag windows, or edge-flag triggers depending on the signal type.
- Added`GET/POST /v1/scan` accepts a SQL WHERE clause and returns matching tickers.
- Added`POST /v1/webhooks` creates a SQL-driven webhook that POSTs to your URL once per minute when new tickers enter the match set. State-change deduplication, HMAC-SHA256 signed deliveries, 5-attempt retry.
- Added50+ boolean flags + ~50 numeric/string columns documented on the schema page.
- AddedCoverage: ~12,000 US-listed equities and the top 100 cryptocurrencies by market cap.