Agents
This page is for developers writing their own agent loop with LangChain, the Vercel AI SDK, the OpenAI SDK, the Anthropic SDK, or raw HTTP. Drop the tool definitions below into your runtime’s tool-use schema.
Before you wire it in
Three things to know before pasting the JSON below.
The grammar. Flat WHERE over ~150 named columns — no JOIN, no GROUP BY, no OVER. Schema fits in a system prompt; one q string drives /scan, /scan?asof=, and every subscribe endpoint.
The loop. User asks in English → agent composes q → call /v2/scan live, ?asof= to replay history, /subscribe to register a webhook. State lives in the API; the agent stays stateless.
The system prompt. Paste in column + flag names from /docs/schema, the grammar bounds (flat WHERE, bare identifiers, fully-specified numeric literals), and the asset-symbol prefixes (X:BTCUSD for crypto — see Asset symbols).
Pick a runtime
Claude tool-use docs for the API surface. Pass the JSON below as the tools array on a Messages request.
All tools, one JSON blob
The whole set in one paste. Use this if your runtime accepts a tool array.
[
{
"name": "tickerbot_list_tickers",
"description": "GET /v2/tickers. List active tickers, paginated. Returns the universe of tracked symbols (~12,000 US equities + top 100 crypto by market cap) in alphabetical order with a slim payload (`ticker`, `name`, `asset_type`, `exchange`, `market_cap`). Use cursor pagination to walk the full list, or pass `?tickers=...` for bulk lookup of named symbols with full rows.",
"input_schema": {
"type": "object",
"properties": {
"tickers": {
"type": "string",
"description": "Comma-separated list of symbols (up to 50). When set, the response returns the full row for each requested symbol; pagination params are ignored. Symbols missing from our universe come back with `_not_found: true`."
},
"limit": {
"type": "integer",
"description": "Page size for the alphabetical walk. Max 1000.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response's `next_cursor` field. Continues the walk from after that page."
},
"search": {
"type": "string",
"description": "Substring filter over `ticker` (case-insensitive) and `name`. When set, results are ordered by `market_cap DESC NULLS LAST`."
},
"asset_type": {
"type": "string",
"description": "Filter by asset type — typically `equity` or `crypto`. Matched case-insensitively against the stored value."
},
"exchange": {
"type": "string",
"description": "Filter by exchange code (uppercase). Common values: `XNYS`, `XNAS`, `BATS`."
},
"sector": {
"type": "string",
"description": "Filter by sector name. Exact match against the stored `sector` field."
},
"min_market_cap": {
"type": "number",
"description": "Minimum market cap (USD). Results are ordered by `market_cap DESC NULLS LAST` when set."
}
}
}
},
{
"name": "tickerbot_get_ticker",
"description": "GET /v2/tickers/{ticker}. Get the full current state of one ticker, or a snapshot at a past date. Returns the entire ticker row. Every field on the schema page, including current values for all numeric signals and the current value of every boolean flag. Pass `?asof=YYYY-MM-DD` to return the row exactly as it was at the close of that day — see the dedicated [as-of snapshot](/docs/endpoints/tickers/history) page for plan-gating, frozen-fields semantics, and the 14-day lookback fallback.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol. Case-insensitive. Equities: exchange symbol (`AAPL`). Crypto: bare symbol (`BTC`)."
},
"asof": {
"type": "string",
"description": "Optional. Returns the row as it was at the close of the given `YYYY-MM-DD` (or full ISO). Sourced from `signal_daily_state`. All paid plans get full history."
}
},
"required": [
"ticker"
]
}
},
{
"name": "tickerbot_get_ticker_history",
"description": "GET /v2/tickers/{ticker}/history. Get the full ticker row as of a past date. Time-travel to any historical date and pull the whole wide row as it stood then, including the boolean flag values, technical indicators, and the most-recent fundamentals known on that date. Useful for \"what did we know\" reconstructions and replaying screens across history.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp `YYYY-MM-DDTHH:MM:SSZ`. The response returns the most-recent daily snapshot on or before this date."
}
},
"required": [
"ticker",
"asof"
]
}
},
{
"name": "tickerbot_get_ticker_bars",
"description": "GET /v2/tickers/{ticker}/bars/{interval}. Open/high/low/close/volume bars for a symbol at a given interval. Simple OHLCV bars from 1-minute through daily, with point-in-time (`asof`) and back-paging (`before`/`limit`). `1d` and `1h` cover the full universe with full history; sub-hour intervals cover the active universe and back-fill on demand. Pass a comma-separated symbol list for a bulk response keyed by symbol.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol."
},
"interval": {
"type": "string",
"description": "Bar interval — one of `1s`, `1m`, `5m`, `15m`, `30m`, `1h`, `1d`."
},
"limit": {
"type": "integer",
"description": "Most-recent N bars.",
"default": 100
},
"before": {
"type": "string",
"description": "Return the N bars ending strictly before this date/timestamp — back-paging."
},
"asof": {
"type": "string",
"description": "Return a single bar as of that date/timestamp (point-in-time)."
},
"cursor": {
"type": "string",
"description": "Continuation token from a prior response's `next_cursor`; sugar for `before`."
}
},
"required": [
"ticker",
"interval"
]
}
},
{
"name": "tickerbot_get_ticker_events",
"description": "GET /v2/tickers/{ticker}/events. Discrete event log for one ticker. Returns dividends, splits, and insider transactions for the ticker, newest-first, in a unified envelope. Each event has a `ts`, a `kind`, and a `payload` whose shape depends on the kind. (Analyst rating changes are a separate feed: `/v2/analyst/events`.)",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"kind": {
"type": "string",
"description": "Filter by event kind. Omit for all three merged into one timeline.",
"enum": [
"dividend",
"split",
"insider"
]
},
"before": {
"type": "string",
"description": "Return events strictly before this date (YYYY-MM-DD). Back-paging cursor; pass the previous response's `next_cursor`."
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000.",
"default": 100
}
},
"required": [
"ticker"
]
}
},
{
"name": "tickerbot_get_ticker_holdings",
"description": "GET /v2/tickers/{ticker}/holdings. Constituent holdings of an ETF, weight-ranked. Returns the ETF's constituents and their weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `holdings` is empty. The reverse lookup (\"which ETFs hold NVDA\") is a `/v2/scan` filter on the `etf_holders` column, not an endpoint.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "ETF symbol."
},
"limit": {
"type": "integer",
"description": "Max holdings returned. Max 5000.",
"default": 500
}
},
"required": [
"ticker"
]
}
},
{
"name": "tickerbot_get_ticker_sectors",
"description": "GET /v2/tickers/{ticker}/sectors. Sector allocation of an ETF, weight-ranked. Returns the ETF's sector weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `sectors` is empty.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "ETF symbol."
}
},
"required": [
"ticker"
]
}
},
{
"name": "tickerbot_post_ticker_subscribe",
"description": "POST /v2/tickers/{ticker}/subscribe. Create a webhook that fires when this ticker matches a condition. Subscribes a single ticker. `condition` is a WHERE-clause fragment evaluated when this ticker is matched — you do not need to add `ticker = '…'` yourself; the endpoint scopes the predicate to the path ticker automatically. Plan-gated by webhook tier; counts against your account-wide webhook cap. Returns the same shape as `GET /v2/webhooks/{id}`.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol (case-insensitive)."
},
"condition": {
"type": "string",
"description": "WHERE-clause fragment using signal/column names from the schema."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard)."
},
"cadence": {
"type": "string",
"description": "How often to evaluate. Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to your plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars). Defaults to `<TICKER>: <condition>`."
}
},
"required": [
"ticker",
"condition"
]
}
},
{
"name": "tickerbot_list_signals_catalog",
"description": "GET /v2/signals. Built-in signals plus your custom signals, each tagged with `kind`. Returns the unified signal catalog: every built-in column on the live schema (tagged `kind: 'builtin'`, carrying `name`, `type`, `source`) plus the caller's own custom signals (tagged `kind: 'expression'`, carrying `name`, `description`, `expr`, `created_at`, `updated_at`).\n\nPagination applies only to the custom-signal slice — built-ins are returned in their entirety on every page since they're a small fixed set.",
"input_schema": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Filter by kind. Omit to return both.",
"enum": [
"builtin",
"custom"
]
},
"limit": {
"type": "integer",
"description": "Page size for the custom-signal slice. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from a prior response."
}
}
}
},
{
"name": "tickerbot_get_signals_match",
"description": "GET /v2/signals/{signal}. Find tickers that match a signal right now, or at a past date with ?asof=. Return the set of tickers where the named signal is true (for boolean flags) or where the numeric value satisfies a single-bounded condition. Sorted by signal value descending for numerics, alphabetically by ticker for booleans. Pass `?asof=YYYY-MM-DD` to get the match set as it was at the close of that day — sourced from `signal_daily_state`.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on `ticker`. Boolean flags (e.g. `golden_cross_today`, `above_sma_50`) are detected automatically; numerics (e.g. `rsi_14`, `market_cap`, `pe_ratio`) require a condition."
},
"condition": {
"type": "string",
"description": "Required for numeric signals. Single bound, format `<op><value>`. Operators: `>`, `>=`, `=`, `!=`, `<`, `<=`. Examples: `>70`, `<=200`, `!=0`. Ignored for boolean flags."
},
"asof": {
"type": "string",
"description": "Optional. Run the match against historical daily state at the close of the given `YYYY-MM-DD` (or full ISO). All paid plans get full history."
},
"universe": {
"type": "string",
"description": "Optional. Scope to a system or caller-owned universe slug."
},
"limit": {
"type": "integer",
"description": "Page size. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"signal"
]
}
},
{
"name": "tickerbot_get_signal_asof",
"description": "GET /v2/signals/{signal}. Run the match set at the close of a past date — same endpoint as live, with ?asof=. Time-travel form of `GET /v2/signals/{signal}`. Pass `?asof=YYYY-MM-DD` (or full ISO) and the server returns the set of tickers where the signal was true (booleans) or where the numeric value satisfied your `condition` at the close of that day. Sourced from `signal_daily_state`. Same response envelope as the live call, with `_meta.resolution: \"daily\"` and `_meta.frozen_fields` listing the columns whose values aren't historized.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on `ticker`. Boolean flags are detected automatically; numerics require a `condition`."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp. The server returns the match set as of the close of this day."
},
"condition": {
"type": "string",
"description": "Required for numeric signals. Single-bounded condition: `<op><value>`. Operators: `>`, `>=`, `=`, `!=`, `<`, `<=`. Ignored for boolean flags."
},
"universe": {
"type": "string",
"description": "Scope to a system or caller-owned universe slug."
},
"limit": {
"type": "integer",
"description": "Page size. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"signal",
"asof"
]
}
},
{
"name": "tickerbot_get_signal_history",
"description": "GET /v2/signals/{signal}/{ticker}/history/{interval}. Time series of one signal for one ticker. Returns a sequence of `{t, v}` bars for the requested signal × ticker at the chosen resolution. `t` is an ISO timestamp (or `YYYY-MM-DD` for daily/weekly). `v` is the signal value at that bar (a number for numeric signals, `true`/`false` for boolean flags).",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on the wide-state tables (matches the schema page 1:1)."
},
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"interval": {
"type": "string",
"description": "Resolution. Daily-only signals (SMAs, RSI, MACD, fundamentals) are not stored at minute/hourly resolution; request `1d` for those. `1q` (quarterly) is a distinct surface: it returns per-fiscal-quarter fundamentals for `eps`, `eps_estimate`, `eps_surprise`, `eps_surprise_pct`, `revenue`, `gross_profit`, and `free_cash_flow` (20+ quarters), with `fiscal_period` on each bar.",
"enum": [
"1m",
"1h",
"1d",
"1w",
"1q"
]
},
"from": {
"type": "string",
"description": "Earliest bar timestamp (inclusive). YYYY-MM-DD or ISO. Defaults to your plan's history window."
},
"to": {
"type": "string",
"description": "Latest bar timestamp (inclusive). YYYY-MM-DD or ISO. Defaults to \"now\"."
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000.",
"default": 252
},
"cursor": {
"type": "string",
"description": "Opaque cursor. Walks back in time page-by-page from the most-recent bar."
}
},
"required": [
"signal",
"ticker",
"interval"
]
}
},
{
"name": "tickerbot_list_signal_events",
"description": "GET /v2/signals/{signal}/{ticker}/events. Run-length-encoded occurrences — windows for state flags, points for event flags. Per-bar booleans (`history`) are useful for historical analysis; per-occurrence records are what you want for chart overlays and dashboards. This endpoint returns each *firing* of a signal on a ticker as a single row.\n\nFor **state**-style signals (`above_sma_50`, `in_uptrend`, `at_52w_high`, …) each row is a true-window: `started_at` is when the condition flipped false→true, `ended_at` is when it flipped back (or null if it's still true). `start_price` and `end_price` carry the price at the open/close of the window.\n\nFor **event**-style signals (`golden_cross_today`, `gap_up`, `breakout`, …) each row is a point firing: `started_at` is when the event fired and `ended_at` is the same value. `start_price` is the price at that bar.\n\nCustom (expression) signals are returned in the state shape — the expression is evaluated per bar and contiguous true runs are collapsed into windows.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "Built-in flag name or your custom signal name."
},
"ticker": {
"type": "string",
"description": "Ticker symbol (case-insensitive)."
},
"from": {
"type": "string",
"description": "Earliest `started_at` (inclusive). ISO timestamp or epoch ms. Defaults to 2 years ago."
},
"to": {
"type": "string",
"description": "Latest `started_at` (inclusive). ISO timestamp or epoch ms. Defaults to now."
},
"limit": {
"type": "integer",
"description": "Page size. Max 2000.",
"default": 500
},
"cursor": {
"type": "string",
"description": "Opaque pagination cursor from a previous response."
}
},
"required": [
"signal",
"ticker"
]
}
},
{
"name": "tickerbot_post_signal_subscribe",
"description": "POST /v2/signals/{signal}/subscribe. Create a webhook that fires when any ticker matches this signal. For boolean signals (e.g. `at_52w_high`, `golden_cross`), the predicate is `signal = true` and `condition` is ignored. For numeric signals (e.g. `rsi_14`, `change_1m`), `condition` is required and takes the shape `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`. Optionally restrict the scope to a single ticker or a universe.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "Signal name from the schema (case-insensitive)."
},
"condition": {
"type": "string",
"description": "Required for numeric signals; ignored for boolean signals. Shape: `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`."
},
"ticker": {
"type": "string",
"description": "Restrict to a single ticker. Default: any ticker."
},
"universe": {
"type": "string",
"description": "Restrict to a system or user-owned universe (e.g. `top_100`)."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when fired. Omit for in-app delivery."
},
"cadence": {
"type": "string",
"description": "Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars)."
}
},
"required": [
"signal"
]
}
},
{
"name": "tickerbot_create_custom_signal",
"description": "POST /v2/signals. Save a named SQL expression you can reference like a built-in signal. Scale and above. Persists a named boolean (or numeric) expression in the existing predicate grammar. Once saved, the name is referenceable from any SQL context — `/v2/scan` `q`, `/v2/signals/{name}`, subscribe-path `condition`, the expression body of another custom signal — and the predicate compiler inlines its body before SQL emit.\n\nThe expression is compiled at create time against the live signal column whitelist; references to other custom signals you own are inlined first (recursion is detected). Names are scoped per-user: `(user_id, name)` is the key. Names cannot collide with built-in column names — those return 409 `name_collision`.\n\nWrite access requires Scale or above. Hobby/Pro can read `/v2/signals` catalog but writes return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Slug — `^[a-z][a-z0-9_]{0,63}$`. Must not collide with any built-in column name."
},
"expr": {
"type": "string",
"description": "SQL expression in the predicate grammar. May reference built-in columns and other custom signals you own. Max 4000 chars."
},
"description": {
"type": "string",
"description": "Free-form notes. Max 500 chars."
}
},
"required": [
"name",
"expr"
]
}
},
{
"name": "tickerbot_patch_custom_signal",
"description": "PATCH /v2/signals/{name}. Edit the expression or description of one of your custom signals. Scale and above. Partial update — supply `expr`, `description`, or both. Providing `expr` recompiles against the live column whitelist (same validator as create). Built-in signals are read-only — only custom signals you own can be patched. Write access requires Scale or above; Hobby/Pro return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Custom signal slug."
},
"expr": {
"type": "string",
"description": "New SQL expression. Re-validated and re-inlined against your other custom signals."
},
"description": {
"type": "string",
"description": "New description."
}
},
"required": [
"name"
]
}
},
{
"name": "tickerbot_delete_custom_signal",
"description": "DELETE /v2/signals/{name}. Remove a custom signal — refused by default if anything references it. Scale and above. By default, DELETE is cascade-safe: it refuses with 409 `signal_referenced` if any of your other custom signals reference this signal by name. The response carries a `referencing_signals` array so you can audit before deciding.\n\nPass `?force=true` to delete anyway. Existing references will compile to `unknown_column` errors next time the consumer recompiles, so use force when you've already fixed the references. Write access requires Scale or above; Hobby/Pro return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Custom signal slug."
},
"force": {
"type": "boolean",
"description": "When `true`, skip the reference check and delete. References will break on next recompile.",
"default": "false"
}
},
"required": [
"name"
]
}
},
{
"name": "tickerbot_get_scan",
"description": "GET /v2/scan. Run a SQL WHERE against the current universe. Filter the live ticker universe with a SQL WHERE clause expressed in our SQL grammar. Returns matching tickers sorted by your chosen column. Columns in `q`, `order`, and `fields` are the customer-facing names listed on /docs/schema and /docs/flags. No translation layer.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression. Max 4000 chars. Semicolons, comments, and write-side keywords (INSERT/UPDATE/DELETE/DROP/etc.) are rejected."
},
"universe": {
"type": "string",
"description": "Slug of a system universe (`top_10`, `top_100`) or one of your own (`/v2/universes`). Scopes the scan to those tickers. When omitted, the scan runs across all ~12,000 tracked tickers."
},
"asof": {
"type": "string",
"description": "Optional. Run the WHERE against historical daily state for the given `YYYY-MM-DD` (or full ISO). Plan history depth applies."
},
"order": {
"type": "string",
"description": "Column to sort by. Must be a valid identifier (lowercase letters, digits, underscore).",
"default": "day_change_pct"
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"fields": {
"type": "string",
"description": "Comma-separated list of extra columns to include in each result row. Default columns are always present: ticker, name, asset_type, price, day_change_pct, gap_pct, relative_volume, market_cap."
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"q"
]
}
},
{
"name": "tickerbot_get_scan_asof",
"description": "GET /v2/scan. Run a SQL WHERE against the close of a past trading day — same endpoint as live, with ?asof=. Time-travel form of `GET /v2/scan`. Pass `?asof=YYYY-MM-DD` (or full ISO) and the server evaluates your `q` against the daily-state snapshot of every ticker on that day. Same SQL grammar, same response envelope as live; the `_meta.resolution: \"daily\"` marker and `_meta.frozen_fields` list signal that the universe was reconstructed from historical state.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression. Same grammar as live scan. Max 4000 chars."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp. The server evaluates the WHERE against the close-of-day snapshot for this date."
},
"universe": {
"type": "string",
"description": "Slug of a system or caller-owned universe. Scopes the scan to those tickers as of the same date."
},
"order": {
"type": "string",
"description": "Column to sort by. Same column whitelist as live scan.",
"default": "day_change_pct"
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"fields": {
"type": "string",
"description": "Comma-separated list of extra columns to include in each result row. Default columns are always present."
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"q",
"asof"
]
}
},
{
"name": "tickerbot_post_scan",
"description": "POST /v2/scan. Same as GET /v2/scan, with `q` in the body. POST mirror of `GET /v2/scan` for queries too long to fit in a URL. Accepts the same parameters (`q`, `order`, `dir`, `fields`, `limit`) in a JSON body. `cursor` continues to come from the query string for consistency with the GET pagination shape.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression."
},
"order": {
"type": "string",
"description": "Sort column."
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
]
},
"fields": {
"type": "string",
"description": "Comma-separated extra columns."
},
"limit": {
"type": "integer",
"description": "Page size, max 100."
},
"cursor": {
"type": "string",
"description": "Opaque cursor (query string, not body)."
}
},
"required": [
"q"
]
}
},
{
"name": "tickerbot_post_scan_subscribe",
"description": "POST /v2/scan/subscribe. Create a webhook that fires when any ticker matches an arbitrary WHERE clause. Subscribes to a free-form `q` (same grammar as `GET /v2/scan`). The webhook delivers the match set every time it changes (tickers entering or leaving). Optionally scope to a `universe`. Max `q` length is 1500 chars.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "WHERE-clause expression using signal/column names. Max 1500 chars."
},
"universe": {
"type": "string",
"description": "System or user-owned universe to scope the scan (e.g. `top_100`)."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when the match set changes. Omit for in-app delivery."
},
"cadence": {
"type": "string",
"description": "Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars). Defaults to `scan: <q>`."
}
},
"required": [
"q"
]
}
},
{
"name": "tickerbot_list_news_scan",
"description": "GET /v2/news/scan. SQL-style scan over the news_article archive. Returns articles or aggregate rollups. One endpoint, two shapes. Without `group_by`, each result is one article (default columns: `id`, `time_published`, `title`, `source`, `tickers`, `overall_sentiment_score`, `overall_sentiment_label`). With `group_by` set, results are aggregate rollups — default SELECT becomes `<group_by cols>, COUNT(*) AS volume`. Use the `tk` alias (auto-UNNEST of `tickers`) to roll up per ticker without writing the join.\n\n\"As-of\" is just a WHERE filter on `time_published` — there is no dedicated asof parameter.\n\nBoth `GET` and `POST` are supported; `POST` body takes the same parameter names for queries too long to fit in a URL.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "WHERE clause over the news_article table. Max 4000 chars."
},
"select": {
"type": "string",
"description": "Comma-separated columns/expressions to return. Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`)."
},
"group_by": {
"type": "string",
"description": "Comma-separated group keys. Switches the response to aggregate rows. Use `tk` to roll up per ticker."
},
"having": {
"type": "string",
"description": "HAVING clause on the aggregate. Requires `group_by`."
},
"order": {
"type": "string",
"description": "Sort column or alias. Defaults to `time_published` (article rows) or `volume` (aggregate rows)."
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000 on Scale/Enterprise.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque pagination cursor from a prior response's `next_cursor`."
}
},
"required": [
"q"
]
}
},
{
"name": "tickerbot_list_webhooks",
"description": "GET /v2/webhooks. List every webhook subscription on this account. Returns webhook subscriptions for the current account (user-scoped, not key-scoped — every key on the same account sees the same registry). Ordered newest-first by `created_at`. Each row carries a `subscription_origin` discriminator naming which subscribe endpoint created it (`ticker` | `signal` | `scan`). Use `?status=` to filter; pass `next_cursor` back via `cursor=` to paginate.",
"input_schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "Filter by status.",
"enum": [
"active",
"pending_verification",
"disabled"
]
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
}
}
},
{
"name": "tickerbot_get_webhook",
"description": "GET /v2/webhooks/{id}. Fetch one webhook subscription. Returns the current state of a webhook subscription. Visibility is account-scoped (not key-scoped) — every API key on this account can fetch every webhook on this account. Webhooks owned by a different account return 404.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.)."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_delete_webhook",
"description": "DELETE /v2/webhooks/{id}. Delete a webhook subscription. Hard delete. Any pending deliveries already enqueued may still fire (they capture the target URL + signing secret at queue time). To pause-rather-than-delete, just stop accepting deliveries at your target; after enough consecutive failures the subscription flips to `disabled` and you can re-enable later via `POST /v2/webhooks/{id}/enable`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_list_webhook_deliveries",
"description": "GET /v2/webhooks/{id}/deliveries. Inspect recent deliveries for a webhook. Returns the most-recent deliveries (pings and fires) for a webhook, newest-first by `created_at`. Useful for diagnosing failures and verifying signatures. The request body sent to your target is not stored on the delivery row — only the metadata (status, attempt, response code, error). Account-scoped: any key on the same account can read any webhook's deliveries.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
},
"status": {
"type": "string",
"description": "Filter by delivery status.",
"enum": [
"pending",
"delivered",
"permanent_failure"
]
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_enable_webhook",
"description": "POST /v2/webhooks/{id}/enable. Re-enable a disabled or pending webhook. Resets a webhook back to `pending_verification`, clears its match-state cache (so the next eval treats every currently-matching ticker as new and fires a single `webhook.fired`), and re-enqueues the handshake ping. On 2xx the webhook flips to `active`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_test_fire_webhook",
"description": "POST /v2/webhooks/{id}/test. Send a one-off synthetic delivery for QA / smoke testing. Writes a pending `webhook.fired` delivery with `test: true` and an empty `matches` array. The scheduler picks it up on its next tick and POSTs the same headers + signature shape as a real firing — the only difference is the `test` flag in the body and on the delivery row. Useful for verifying your receiver is reachable, parses the payload correctly, and verifies the signature.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_list_universes",
"description": "GET /v2/universes. List your universes. A universe is a named list of tickers you can reference from `/v2/scan?universe=`, `/v2/signals/{signal}?universe=`, and `/v2/tickers?universe=`, or pass as `universe_id` when subscribing on a resource. System universes (`top_10` and `top_100`, the top-N most-actively-traded tickers, rebalanced monthly) are available to every account at `GET /v2/universes/system`.",
"input_schema": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
}
}
},
{
"name": "tickerbot_create_universe",
"description": "POST /v2/universes. Create a universe. Create a named ticker list owned by the calling account. Body: `{ id?, name, description?, tickers }`. If `id` is omitted, a slug is generated.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Optional slug. Lowercase letters, digits, underscore. Must be unique within your account."
},
"name": {
"type": "string",
"description": "Human-readable label."
},
"description": {
"type": "string",
"description": "Free-form notes."
},
"tickers": {
"type": "array",
"description": "Ticker symbols. Validated against your plan scope + the active universe.",
"items": {
"type": "string"
}
}
},
"required": [
"name",
"tickers"
]
}
},
{
"name": "tickerbot_list_system_universes",
"description": "GET /v2/universes/system. List system universes (top_10, top_100). System universes are read-only ticker lists rebalanced monthly by 30-day trailing dollar volume. Available to every account regardless of plan.",
"input_schema": {
"type": "object",
"properties": {}
}
},
{
"name": "tickerbot_get_universe",
"description": "GET /v2/universes/{id}. Fetch a single universe. Returns the universe doc. Use `top_10`/`top_100` to fetch a system universe; any other slug must be one your account owns.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_patch_universe",
"description": "PATCH /v2/universes/{id}. Update a universe. Update `name`, `description`, or `tickers`. To add/remove ticker subsets without replacing the full list, pass `{ add: [...], remove: [...] }` instead of `tickers`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
},
"name": {
"type": "string",
"description": "New label."
},
"description": {
"type": "string",
"description": "New notes."
},
"tickers": {
"type": "array",
"description": "Replace the full ticker list.",
"items": {
"type": "string"
}
},
"add": {
"type": "array",
"description": "Add these tickers (deduplicated).",
"items": {
"type": "string"
}
},
"remove": {
"type": "array",
"description": "Remove these tickers.",
"items": {
"type": "string"
}
}
},
"required": [
"id"
]
}
},
{
"name": "tickerbot_delete_universe",
"description": "DELETE /v2/universes/{id}. Delete a universe. Permanently delete one of your universes. System universes (`top_10`, `top_100`) cannot be deleted — they return `403 system_universe_immutable`. Subscribed webhooks that reference the deleted universe will fail on their next eval with `unknown_universe`; clean them up first via `/v2/webhooks/{id}` and re-subscribe.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
}
},
"required": [
"id"
]
}
}
]Or: fetch the catalog at runtime
Don't embed the JSON above — pull the live catalog from the server, stays current as we add tools.
Tickerbot publishes the canonical tool catalog as a public, unauthenticated discovery endpoint. GET it once at agent startup and your runtime always reflects the latest surface — same source the @tickerbot/mcp-server npm package reads on subprocess spawn.
curl "https://api.tickerbot.io/mcp/tools"Response shape: { asOf, serverInfo, protocolVersion, tools }. The tools array is exactly what tools/list returns over MCP, including the dispatch metadata (endpoint.method, endpoint.path, endpoint.paramLocation) so your runtime can route tool calls to the right HTTP request without hard-coding the mapping.
When a new tool ships on Tickerbot, your agent picks it up on the next startup — no embedded JSON to update, no republish, no PR. Recommended over pasting the static blob above for anything beyond throwaway demos.
Per-tool reference
One block per tool, indexed by name.
tickerbot_list_tickers
GET /v2/tickers
{
"name": "tickerbot_list_tickers",
"description": "GET /v2/tickers. List active tickers, paginated. Returns the universe of tracked symbols (~12,000 US equities + top 100 crypto by market cap) in alphabetical order with a slim payload (`ticker`, `name`, `asset_type`, `exchange`, `market_cap`). Use cursor pagination to walk the full list, or pass `?tickers=...` for bulk lookup of named symbols with full rows.",
"input_schema": {
"type": "object",
"properties": {
"tickers": {
"type": "string",
"description": "Comma-separated list of symbols (up to 50). When set, the response returns the full row for each requested symbol; pagination params are ignored. Symbols missing from our universe come back with `_not_found: true`."
},
"limit": {
"type": "integer",
"description": "Page size for the alphabetical walk. Max 1000.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response's `next_cursor` field. Continues the walk from after that page."
},
"search": {
"type": "string",
"description": "Substring filter over `ticker` (case-insensitive) and `name`. When set, results are ordered by `market_cap DESC NULLS LAST`."
},
"asset_type": {
"type": "string",
"description": "Filter by asset type — typically `equity` or `crypto`. Matched case-insensitively against the stored value."
},
"exchange": {
"type": "string",
"description": "Filter by exchange code (uppercase). Common values: `XNYS`, `XNAS`, `BATS`."
},
"sector": {
"type": "string",
"description": "Filter by sector name. Exact match against the stored `sector` field."
},
"min_market_cap": {
"type": "number",
"description": "Minimum market cap (USD). Results are ordered by `market_cap DESC NULLS LAST` when set."
}
}
}
}tickerbot_get_ticker
GET /v2/tickers/{ticker}
{
"name": "tickerbot_get_ticker",
"description": "GET /v2/tickers/{ticker}. Get the full current state of one ticker, or a snapshot at a past date. Returns the entire ticker row. Every field on the schema page, including current values for all numeric signals and the current value of every boolean flag. Pass `?asof=YYYY-MM-DD` to return the row exactly as it was at the close of that day — see the dedicated [as-of snapshot](/docs/endpoints/tickers/history) page for plan-gating, frozen-fields semantics, and the 14-day lookback fallback.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol. Case-insensitive. Equities: exchange symbol (`AAPL`). Crypto: bare symbol (`BTC`)."
},
"asof": {
"type": "string",
"description": "Optional. Returns the row as it was at the close of the given `YYYY-MM-DD` (or full ISO). Sourced from `signal_daily_state`. All paid plans get full history."
}
},
"required": [
"ticker"
]
}
}tickerbot_get_ticker_history
GET /v2/tickers/{ticker}/history
{
"name": "tickerbot_get_ticker_history",
"description": "GET /v2/tickers/{ticker}/history. Get the full ticker row as of a past date. Time-travel to any historical date and pull the whole wide row as it stood then, including the boolean flag values, technical indicators, and the most-recent fundamentals known on that date. Useful for \"what did we know\" reconstructions and replaying screens across history.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp `YYYY-MM-DDTHH:MM:SSZ`. The response returns the most-recent daily snapshot on or before this date."
}
},
"required": [
"ticker",
"asof"
]
}
}tickerbot_get_ticker_bars
GET /v2/tickers/{ticker}/bars/{interval}
{
"name": "tickerbot_get_ticker_bars",
"description": "GET /v2/tickers/{ticker}/bars/{interval}. Open/high/low/close/volume bars for a symbol at a given interval. Simple OHLCV bars from 1-minute through daily, with point-in-time (`asof`) and back-paging (`before`/`limit`). `1d` and `1h` cover the full universe with full history; sub-hour intervals cover the active universe and back-fill on demand. Pass a comma-separated symbol list for a bulk response keyed by symbol.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol."
},
"interval": {
"type": "string",
"description": "Bar interval — one of `1s`, `1m`, `5m`, `15m`, `30m`, `1h`, `1d`."
},
"limit": {
"type": "integer",
"description": "Most-recent N bars.",
"default": 100
},
"before": {
"type": "string",
"description": "Return the N bars ending strictly before this date/timestamp — back-paging."
},
"asof": {
"type": "string",
"description": "Return a single bar as of that date/timestamp (point-in-time)."
},
"cursor": {
"type": "string",
"description": "Continuation token from a prior response's `next_cursor`; sugar for `before`."
}
},
"required": [
"ticker",
"interval"
]
}
}tickerbot_get_ticker_events
GET /v2/tickers/{ticker}/events
{
"name": "tickerbot_get_ticker_events",
"description": "GET /v2/tickers/{ticker}/events. Discrete event log for one ticker. Returns dividends, splits, and insider transactions for the ticker, newest-first, in a unified envelope. Each event has a `ts`, a `kind`, and a `payload` whose shape depends on the kind. (Analyst rating changes are a separate feed: `/v2/analyst/events`.)",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"kind": {
"type": "string",
"description": "Filter by event kind. Omit for all three merged into one timeline.",
"enum": [
"dividend",
"split",
"insider"
]
},
"before": {
"type": "string",
"description": "Return events strictly before this date (YYYY-MM-DD). Back-paging cursor; pass the previous response's `next_cursor`."
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000.",
"default": 100
}
},
"required": [
"ticker"
]
}
}tickerbot_get_ticker_holdings
GET /v2/tickers/{ticker}/holdings
{
"name": "tickerbot_get_ticker_holdings",
"description": "GET /v2/tickers/{ticker}/holdings. Constituent holdings of an ETF, weight-ranked. Returns the ETF's constituents and their weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `holdings` is empty. The reverse lookup (\"which ETFs hold NVDA\") is a `/v2/scan` filter on the `etf_holders` column, not an endpoint.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "ETF symbol."
},
"limit": {
"type": "integer",
"description": "Max holdings returned. Max 5000.",
"default": 500
}
},
"required": [
"ticker"
]
}
}tickerbot_get_ticker_sectors
GET /v2/tickers/{ticker}/sectors
{
"name": "tickerbot_get_ticker_sectors",
"description": "GET /v2/tickers/{ticker}/sectors. Sector allocation of an ETF, weight-ranked. Returns the ETF's sector weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `sectors` is empty.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "ETF symbol."
}
},
"required": [
"ticker"
]
}
}tickerbot_post_ticker_subscribe
POST /v2/tickers/{ticker}/subscribe
{
"name": "tickerbot_post_ticker_subscribe",
"description": "POST /v2/tickers/{ticker}/subscribe. Create a webhook that fires when this ticker matches a condition. Subscribes a single ticker. `condition` is a WHERE-clause fragment evaluated when this ticker is matched — you do not need to add `ticker = '…'` yourself; the endpoint scopes the predicate to the path ticker automatically. Plan-gated by webhook tier; counts against your account-wide webhook cap. Returns the same shape as `GET /v2/webhooks/{id}`.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Ticker symbol (case-insensitive)."
},
"condition": {
"type": "string",
"description": "WHERE-clause fragment using signal/column names from the schema."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard)."
},
"cadence": {
"type": "string",
"description": "How often to evaluate. Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to your plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars). Defaults to `<TICKER>: <condition>`."
}
},
"required": [
"ticker",
"condition"
]
}
}tickerbot_list_signals_catalog
GET /v2/signals
{
"name": "tickerbot_list_signals_catalog",
"description": "GET /v2/signals. Built-in signals plus your custom signals, each tagged with `kind`. Returns the unified signal catalog: every built-in column on the live schema (tagged `kind: 'builtin'`, carrying `name`, `type`, `source`) plus the caller's own custom signals (tagged `kind: 'expression'`, carrying `name`, `description`, `expr`, `created_at`, `updated_at`).\n\nPagination applies only to the custom-signal slice — built-ins are returned in their entirety on every page since they're a small fixed set.",
"input_schema": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Filter by kind. Omit to return both.",
"enum": [
"builtin",
"custom"
]
},
"limit": {
"type": "integer",
"description": "Page size for the custom-signal slice. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from a prior response."
}
}
}
}tickerbot_get_signals_match
GET /v2/signals/{signal}
{
"name": "tickerbot_get_signals_match",
"description": "GET /v2/signals/{signal}. Find tickers that match a signal right now, or at a past date with ?asof=. Return the set of tickers where the named signal is true (for boolean flags) or where the numeric value satisfies a single-bounded condition. Sorted by signal value descending for numerics, alphabetically by ticker for booleans. Pass `?asof=YYYY-MM-DD` to get the match set as it was at the close of that day — sourced from `signal_daily_state`.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on `ticker`. Boolean flags (e.g. `golden_cross_today`, `above_sma_50`) are detected automatically; numerics (e.g. `rsi_14`, `market_cap`, `pe_ratio`) require a condition."
},
"condition": {
"type": "string",
"description": "Required for numeric signals. Single bound, format `<op><value>`. Operators: `>`, `>=`, `=`, `!=`, `<`, `<=`. Examples: `>70`, `<=200`, `!=0`. Ignored for boolean flags."
},
"asof": {
"type": "string",
"description": "Optional. Run the match against historical daily state at the close of the given `YYYY-MM-DD` (or full ISO). All paid plans get full history."
},
"universe": {
"type": "string",
"description": "Optional. Scope to a system or caller-owned universe slug."
},
"limit": {
"type": "integer",
"description": "Page size. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"signal"
]
}
}tickerbot_get_signal_asof
GET /v2/signals/{signal}
{
"name": "tickerbot_get_signal_asof",
"description": "GET /v2/signals/{signal}. Run the match set at the close of a past date — same endpoint as live, with ?asof=. Time-travel form of `GET /v2/signals/{signal}`. Pass `?asof=YYYY-MM-DD` (or full ISO) and the server returns the set of tickers where the signal was true (booleans) or where the numeric value satisfied your `condition` at the close of that day. Sourced from `signal_daily_state`. Same response envelope as the live call, with `_meta.resolution: \"daily\"` and `_meta.frozen_fields` listing the columns whose values aren't historized.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on `ticker`. Boolean flags are detected automatically; numerics require a `condition`."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp. The server returns the match set as of the close of this day."
},
"condition": {
"type": "string",
"description": "Required for numeric signals. Single-bounded condition: `<op><value>`. Operators: `>`, `>=`, `=`, `!=`, `<`, `<=`. Ignored for boolean flags."
},
"universe": {
"type": "string",
"description": "Scope to a system or caller-owned universe slug."
},
"limit": {
"type": "integer",
"description": "Page size. Max 200.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"signal",
"asof"
]
}
}tickerbot_get_signal_history
GET /v2/signals/{signal}/{ticker}/history/{interval}
{
"name": "tickerbot_get_signal_history",
"description": "GET /v2/signals/{signal}/{ticker}/history/{interval}. Time series of one signal for one ticker. Returns a sequence of `{t, v}` bars for the requested signal × ticker at the chosen resolution. `t` is an ISO timestamp (or `YYYY-MM-DD` for daily/weekly). `v` is the signal value at that bar (a number for numeric signals, `true`/`false` for boolean flags).",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "A column on the wide-state tables (matches the schema page 1:1)."
},
"ticker": {
"type": "string",
"description": "Ticker symbol."
},
"interval": {
"type": "string",
"description": "Resolution. Daily-only signals (SMAs, RSI, MACD, fundamentals) are not stored at minute/hourly resolution; request `1d` for those. `1q` (quarterly) is a distinct surface: it returns per-fiscal-quarter fundamentals for `eps`, `eps_estimate`, `eps_surprise`, `eps_surprise_pct`, `revenue`, `gross_profit`, and `free_cash_flow` (20+ quarters), with `fiscal_period` on each bar.",
"enum": [
"1m",
"1h",
"1d",
"1w",
"1q"
]
},
"from": {
"type": "string",
"description": "Earliest bar timestamp (inclusive). YYYY-MM-DD or ISO. Defaults to your plan's history window."
},
"to": {
"type": "string",
"description": "Latest bar timestamp (inclusive). YYYY-MM-DD or ISO. Defaults to \"now\"."
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000.",
"default": 252
},
"cursor": {
"type": "string",
"description": "Opaque cursor. Walks back in time page-by-page from the most-recent bar."
}
},
"required": [
"signal",
"ticker",
"interval"
]
}
}tickerbot_list_signal_events
GET /v2/signals/{signal}/{ticker}/events
{
"name": "tickerbot_list_signal_events",
"description": "GET /v2/signals/{signal}/{ticker}/events. Run-length-encoded occurrences — windows for state flags, points for event flags. Per-bar booleans (`history`) are useful for historical analysis; per-occurrence records are what you want for chart overlays and dashboards. This endpoint returns each *firing* of a signal on a ticker as a single row.\n\nFor **state**-style signals (`above_sma_50`, `in_uptrend`, `at_52w_high`, …) each row is a true-window: `started_at` is when the condition flipped false→true, `ended_at` is when it flipped back (or null if it's still true). `start_price` and `end_price` carry the price at the open/close of the window.\n\nFor **event**-style signals (`golden_cross_today`, `gap_up`, `breakout`, …) each row is a point firing: `started_at` is when the event fired and `ended_at` is the same value. `start_price` is the price at that bar.\n\nCustom (expression) signals are returned in the state shape — the expression is evaluated per bar and contiguous true runs are collapsed into windows.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "Built-in flag name or your custom signal name."
},
"ticker": {
"type": "string",
"description": "Ticker symbol (case-insensitive)."
},
"from": {
"type": "string",
"description": "Earliest `started_at` (inclusive). ISO timestamp or epoch ms. Defaults to 2 years ago."
},
"to": {
"type": "string",
"description": "Latest `started_at` (inclusive). ISO timestamp or epoch ms. Defaults to now."
},
"limit": {
"type": "integer",
"description": "Page size. Max 2000.",
"default": 500
},
"cursor": {
"type": "string",
"description": "Opaque pagination cursor from a previous response."
}
},
"required": [
"signal",
"ticker"
]
}
}tickerbot_post_signal_subscribe
POST /v2/signals/{signal}/subscribe
{
"name": "tickerbot_post_signal_subscribe",
"description": "POST /v2/signals/{signal}/subscribe. Create a webhook that fires when any ticker matches this signal. For boolean signals (e.g. `at_52w_high`, `golden_cross`), the predicate is `signal = true` and `condition` is ignored. For numeric signals (e.g. `rsi_14`, `change_1m`), `condition` is required and takes the shape `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`. Optionally restrict the scope to a single ticker or a universe.",
"input_schema": {
"type": "object",
"properties": {
"signal": {
"type": "string",
"description": "Signal name from the schema (case-insensitive)."
},
"condition": {
"type": "string",
"description": "Required for numeric signals; ignored for boolean signals. Shape: `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`."
},
"ticker": {
"type": "string",
"description": "Restrict to a single ticker. Default: any ticker."
},
"universe": {
"type": "string",
"description": "Restrict to a system or user-owned universe (e.g. `top_100`)."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when fired. Omit for in-app delivery."
},
"cadence": {
"type": "string",
"description": "Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars)."
}
},
"required": [
"signal"
]
}
}tickerbot_create_custom_signal
POST /v2/signals
{
"name": "tickerbot_create_custom_signal",
"description": "POST /v2/signals. Save a named SQL expression you can reference like a built-in signal. Scale and above. Persists a named boolean (or numeric) expression in the existing predicate grammar. Once saved, the name is referenceable from any SQL context — `/v2/scan` `q`, `/v2/signals/{name}`, subscribe-path `condition`, the expression body of another custom signal — and the predicate compiler inlines its body before SQL emit.\n\nThe expression is compiled at create time against the live signal column whitelist; references to other custom signals you own are inlined first (recursion is detected). Names are scoped per-user: `(user_id, name)` is the key. Names cannot collide with built-in column names — those return 409 `name_collision`.\n\nWrite access requires Scale or above. Hobby/Pro can read `/v2/signals` catalog but writes return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Slug — `^[a-z][a-z0-9_]{0,63}$`. Must not collide with any built-in column name."
},
"expr": {
"type": "string",
"description": "SQL expression in the predicate grammar. May reference built-in columns and other custom signals you own. Max 4000 chars."
},
"description": {
"type": "string",
"description": "Free-form notes. Max 500 chars."
}
},
"required": [
"name",
"expr"
]
}
}tickerbot_patch_custom_signal
PATCH /v2/signals/{name}
{
"name": "tickerbot_patch_custom_signal",
"description": "PATCH /v2/signals/{name}. Edit the expression or description of one of your custom signals. Scale and above. Partial update — supply `expr`, `description`, or both. Providing `expr` recompiles against the live column whitelist (same validator as create). Built-in signals are read-only — only custom signals you own can be patched. Write access requires Scale or above; Hobby/Pro return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Custom signal slug."
},
"expr": {
"type": "string",
"description": "New SQL expression. Re-validated and re-inlined against your other custom signals."
},
"description": {
"type": "string",
"description": "New description."
}
},
"required": [
"name"
]
}
}tickerbot_delete_custom_signal
DELETE /v2/signals/{name}
{
"name": "tickerbot_delete_custom_signal",
"description": "DELETE /v2/signals/{name}. Remove a custom signal — refused by default if anything references it. Scale and above. By default, DELETE is cascade-safe: it refuses with 409 `signal_referenced` if any of your other custom signals reference this signal by name. The response carries a `referencing_signals` array so you can audit before deciding.\n\nPass `?force=true` to delete anyway. Existing references will compile to `unknown_column` errors next time the consumer recompiles, so use force when you've already fixed the references. Write access requires Scale or above; Hobby/Pro return `403 custom_signals_tier_required`.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Custom signal slug."
},
"force": {
"type": "boolean",
"description": "When `true`, skip the reference check and delete. References will break on next recompile.",
"default": "false"
}
},
"required": [
"name"
]
}
}tickerbot_get_scan
GET /v2/scan
{
"name": "tickerbot_get_scan",
"description": "GET /v2/scan. Run a SQL WHERE against the current universe. Filter the live ticker universe with a SQL WHERE clause expressed in our SQL grammar. Returns matching tickers sorted by your chosen column. Columns in `q`, `order`, and `fields` are the customer-facing names listed on /docs/schema and /docs/flags. No translation layer.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression. Max 4000 chars. Semicolons, comments, and write-side keywords (INSERT/UPDATE/DELETE/DROP/etc.) are rejected."
},
"universe": {
"type": "string",
"description": "Slug of a system universe (`top_10`, `top_100`) or one of your own (`/v2/universes`). Scopes the scan to those tickers. When omitted, the scan runs across all ~12,000 tracked tickers."
},
"asof": {
"type": "string",
"description": "Optional. Run the WHERE against historical daily state for the given `YYYY-MM-DD` (or full ISO). Plan history depth applies."
},
"order": {
"type": "string",
"description": "Column to sort by. Must be a valid identifier (lowercase letters, digits, underscore).",
"default": "day_change_pct"
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"fields": {
"type": "string",
"description": "Comma-separated list of extra columns to include in each result row. Default columns are always present: ticker, name, asset_type, price, day_change_pct, gap_pct, relative_volume, market_cap."
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"q"
]
}
}tickerbot_get_scan_asof
GET /v2/scan
{
"name": "tickerbot_get_scan_asof",
"description": "GET /v2/scan. Run a SQL WHERE against the close of a past trading day — same endpoint as live, with ?asof=. Time-travel form of `GET /v2/scan`. Pass `?asof=YYYY-MM-DD` (or full ISO) and the server evaluates your `q` against the daily-state snapshot of every ticker on that day. Same SQL grammar, same response envelope as live; the `_meta.resolution: \"daily\"` marker and `_meta.frozen_fields` list signal that the universe was reconstructed from historical state.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression. Same grammar as live scan. Max 4000 chars."
},
"asof": {
"type": "string",
"description": "Target date as `YYYY-MM-DD` or full ISO timestamp. The server evaluates the WHERE against the close-of-day snapshot for this date."
},
"universe": {
"type": "string",
"description": "Slug of a system or caller-owned universe. Scopes the scan to those tickers as of the same date."
},
"order": {
"type": "string",
"description": "Column to sort by. Same column whitelist as live scan.",
"default": "day_change_pct"
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"fields": {
"type": "string",
"description": "Comma-separated list of extra columns to include in each result row. Default columns are always present."
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
},
"required": [
"q",
"asof"
]
}
}tickerbot_post_scan
POST /v2/scan
{
"name": "tickerbot_post_scan",
"description": "POST /v2/scan. Same as GET /v2/scan, with `q` in the body. POST mirror of `GET /v2/scan` for queries too long to fit in a URL. Accepts the same parameters (`q`, `order`, `dir`, `fields`, `limit`) in a JSON body. `cursor` continues to come from the query string for consistency with the GET pagination shape.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "SQL WHERE expression."
},
"order": {
"type": "string",
"description": "Sort column."
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
]
},
"fields": {
"type": "string",
"description": "Comma-separated extra columns."
},
"limit": {
"type": "integer",
"description": "Page size, max 100."
},
"cursor": {
"type": "string",
"description": "Opaque cursor (query string, not body)."
}
},
"required": [
"q"
]
}
}tickerbot_post_scan_subscribe
POST /v2/scan/subscribe
{
"name": "tickerbot_post_scan_subscribe",
"description": "POST /v2/scan/subscribe. Create a webhook that fires when any ticker matches an arbitrary WHERE clause. Subscribes to a free-form `q` (same grammar as `GET /v2/scan`). The webhook delivers the match set every time it changes (tickers entering or leaving). Optionally scope to a `universe`. Max `q` length is 1500 chars.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "WHERE-clause expression using signal/column names. Max 1500 chars."
},
"universe": {
"type": "string",
"description": "System or user-owned universe to scope the scan (e.g. `top_100`)."
},
"target_url": {
"type": "string",
"description": "https:// URL to POST when the match set changes. Omit for in-app delivery."
},
"cadence": {
"type": "string",
"description": "Allowed: `1m`, `5m`, `15m`, `hourly`, `nyse_open`. Defaults to plan max.",
"enum": [
"1m",
"5m",
"15m",
"hourly",
"nyse_open"
]
},
"name": {
"type": "string",
"description": "Human-readable label (up to 80 chars). Defaults to `scan: <q>`."
}
},
"required": [
"q"
]
}
}tickerbot_list_news_scan
GET /v2/news/scan
{
"name": "tickerbot_list_news_scan",
"description": "GET /v2/news/scan. SQL-style scan over the news_article archive. Returns articles or aggregate rollups. One endpoint, two shapes. Without `group_by`, each result is one article (default columns: `id`, `time_published`, `title`, `source`, `tickers`, `overall_sentiment_score`, `overall_sentiment_label`). With `group_by` set, results are aggregate rollups — default SELECT becomes `<group_by cols>, COUNT(*) AS volume`. Use the `tk` alias (auto-UNNEST of `tickers`) to roll up per ticker without writing the join.\n\n\"As-of\" is just a WHERE filter on `time_published` — there is no dedicated asof parameter.\n\nBoth `GET` and `POST` are supported; `POST` body takes the same parameter names for queries too long to fit in a URL.",
"input_schema": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "WHERE clause over the news_article table. Max 4000 chars."
},
"select": {
"type": "string",
"description": "Comma-separated columns/expressions to return. Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`)."
},
"group_by": {
"type": "string",
"description": "Comma-separated group keys. Switches the response to aggregate rows. Use `tk` to roll up per ticker."
},
"having": {
"type": "string",
"description": "HAVING clause on the aggregate. Requires `group_by`."
},
"order": {
"type": "string",
"description": "Sort column or alias. Defaults to `time_published` (article rows) or `volume` (aggregate rows)."
},
"dir": {
"type": "string",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"default": "desc"
},
"limit": {
"type": "integer",
"description": "Page size. Max 1000 on Scale/Enterprise.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque pagination cursor from a prior response's `next_cursor`."
}
},
"required": [
"q"
]
}
}tickerbot_list_webhooks
GET /v2/webhooks
{
"name": "tickerbot_list_webhooks",
"description": "GET /v2/webhooks. List every webhook subscription on this account. Returns webhook subscriptions for the current account (user-scoped, not key-scoped — every key on the same account sees the same registry). Ordered newest-first by `created_at`. Each row carries a `subscription_origin` discriminator naming which subscribe endpoint created it (`ticker` | `signal` | `scan`). Use `?status=` to filter; pass `next_cursor` back via `cursor=` to paginate.",
"input_schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "Filter by status.",
"enum": [
"active",
"pending_verification",
"disabled"
]
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
}
}
}tickerbot_get_webhook
GET /v2/webhooks/{id}
{
"name": "tickerbot_get_webhook",
"description": "GET /v2/webhooks/{id}. Fetch one webhook subscription. Returns the current state of a webhook subscription. Visibility is account-scoped (not key-scoped) — every API key on this account can fetch every webhook on this account. Webhooks owned by a different account return 404.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.)."
}
},
"required": [
"id"
]
}
}tickerbot_delete_webhook
DELETE /v2/webhooks/{id}
{
"name": "tickerbot_delete_webhook",
"description": "DELETE /v2/webhooks/{id}. Delete a webhook subscription. Hard delete. Any pending deliveries already enqueued may still fire (they capture the target URL + signing secret at queue time). To pause-rather-than-delete, just stop accepting deliveries at your target; after enough consecutive failures the subscription flips to `disabled` and you can re-enable later via `POST /v2/webhooks/{id}/enable`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
}tickerbot_list_webhook_deliveries
GET /v2/webhooks/{id}/deliveries
{
"name": "tickerbot_list_webhook_deliveries",
"description": "GET /v2/webhooks/{id}/deliveries. Inspect recent deliveries for a webhook. Returns the most-recent deliveries (pings and fires) for a webhook, newest-first by `created_at`. Useful for diagnosing failures and verifying signatures. The request body sent to your target is not stored on the delivery row — only the metadata (status, attempt, response code, error). Account-scoped: any key on the same account can read any webhook's deliveries.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
},
"status": {
"type": "string",
"description": "Filter by delivery status.",
"enum": [
"pending",
"delivered",
"permanent_failure"
]
},
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor."
}
},
"required": [
"id"
]
}
}tickerbot_enable_webhook
POST /v2/webhooks/{id}/enable
{
"name": "tickerbot_enable_webhook",
"description": "POST /v2/webhooks/{id}/enable. Re-enable a disabled or pending webhook. Resets a webhook back to `pending_verification`, clears its match-state cache (so the next eval treats every currently-matching ticker as new and fires a single `webhook.fired`), and re-enqueues the handshake ping. On 2xx the webhook flips to `active`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
}tickerbot_test_fire_webhook
POST /v2/webhooks/{id}/test
{
"name": "tickerbot_test_fire_webhook",
"description": "POST /v2/webhooks/{id}/test. Send a one-off synthetic delivery for QA / smoke testing. Writes a pending `webhook.fired` delivery with `test: true` and an empty `matches` array. The scheduler picks it up on its next tick and POSTs the same headers + signature shape as a real firing — the only difference is the `test` flag in the body and on the delivery row. Useful for verifying your receiver is reachable, parses the payload correctly, and verifies the signature.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Webhook id."
}
},
"required": [
"id"
]
}
}tickerbot_list_universes
GET /v2/universes
{
"name": "tickerbot_list_universes",
"description": "GET /v2/universes. List your universes. A universe is a named list of tickers you can reference from `/v2/scan?universe=`, `/v2/signals/{signal}?universe=`, and `/v2/tickers?universe=`, or pass as `universe_id` when subscribing on a resource. System universes (`top_10` and `top_100`, the top-N most-actively-traded tickers, rebalanced monthly) are available to every account at `GET /v2/universes/system`.",
"input_schema": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Page size. Max 100.",
"default": 50
},
"cursor": {
"type": "string",
"description": "Opaque cursor from the previous response."
}
}
}
}tickerbot_create_universe
POST /v2/universes
{
"name": "tickerbot_create_universe",
"description": "POST /v2/universes. Create a universe. Create a named ticker list owned by the calling account. Body: `{ id?, name, description?, tickers }`. If `id` is omitted, a slug is generated.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Optional slug. Lowercase letters, digits, underscore. Must be unique within your account."
},
"name": {
"type": "string",
"description": "Human-readable label."
},
"description": {
"type": "string",
"description": "Free-form notes."
},
"tickers": {
"type": "array",
"description": "Ticker symbols. Validated against your plan scope + the active universe.",
"items": {
"type": "string"
}
}
},
"required": [
"name",
"tickers"
]
}
}tickerbot_list_system_universes
GET /v2/universes/system
{
"name": "tickerbot_list_system_universes",
"description": "GET /v2/universes/system. List system universes (top_10, top_100). System universes are read-only ticker lists rebalanced monthly by 30-day trailing dollar volume. Available to every account regardless of plan.",
"input_schema": {
"type": "object",
"properties": {}
}
}tickerbot_get_universe
GET /v2/universes/{id}
{
"name": "tickerbot_get_universe",
"description": "GET /v2/universes/{id}. Fetch a single universe. Returns the universe doc. Use `top_10`/`top_100` to fetch a system universe; any other slug must be one your account owns.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
}
},
"required": [
"id"
]
}
}tickerbot_patch_universe
PATCH /v2/universes/{id}
{
"name": "tickerbot_patch_universe",
"description": "PATCH /v2/universes/{id}. Update a universe. Update `name`, `description`, or `tickers`. To add/remove ticker subsets without replacing the full list, pass `{ add: [...], remove: [...] }` instead of `tickers`.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
},
"name": {
"type": "string",
"description": "New label."
},
"description": {
"type": "string",
"description": "New notes."
},
"tickers": {
"type": "array",
"description": "Replace the full ticker list.",
"items": {
"type": "string"
}
},
"add": {
"type": "array",
"description": "Add these tickers (deduplicated).",
"items": {
"type": "string"
}
},
"remove": {
"type": "array",
"description": "Remove these tickers.",
"items": {
"type": "string"
}
}
},
"required": [
"id"
]
}
}tickerbot_delete_universe
DELETE /v2/universes/{id}
{
"name": "tickerbot_delete_universe",
"description": "DELETE /v2/universes/{id}. Delete a universe. Permanently delete one of your universes. System universes (`top_10`, `top_100`) cannot be deleted — they return `403 system_universe_immutable`. Subscribed webhooks that reference the deleted universe will fail on their next eval with `unknown_universe`; clean them up first via `/v2/webhooks/{id}` and re-subscribe.",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Universe slug."
}
},
"required": [
"id"
]
}
}How to wire it up
The runtime calls the tool. Your code forwards to api.tickerbot.io.
When the LLM decides to call a tool, your code receives the tool name and arguments. Route the call to the matching Tickerbot endpoint (the endpoint field on each tool body above, or hard-coded in your handler), forward the bearer token, and return the JSON response to the LLM.
The OpenAPI spec covers anything generator-driven if your runtime isn't one of the three above.