# GET /v2/signals

**Signal catalog**

Every signal you can name in a query — the built-in signals and your own custom signals, in one catalog.

## Query / path parameters

| Name | In | Type | Required | Description |
|------|----|----|----------|-------------|
| `kind` | query | string | no | Filter by kind (`expression` accepted as a legacy alias for `custom`). Omit to return both. Enum: `builtin`, `custom`. |
| `limit` | query | integer | no | Page size for the custom-signal slice. Max 200. Default: `50`. Example: `10`. |
| `cursor` | query | string | no | Opaque cursor from a prior response. |

## Returns

- `as_of` (string) — Server time this response was assembled (ISO 8601).
- `count` (number) — Rows in this page — the canonical count, equal to `count_builtin` + `count_custom`.
- `count_builtin` (number) — Built-in signals in the catalog.
- `count_custom` (number) — Your custom signals.
- `next_cursor` (string) — Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.
- `taxonomy` (object) — Absent when the page holds no built-ins (`kind=custom`). The signal taxonomy tree, once per response: `groups[]` in derivation-ladder order (record → behavior → indicator → company side), each with `slug`, `label`, `derivation`, `description`, and its `categories[]` (`slug`, `label`, `description`). Definitions live here and only here — rows carry pointers, never the descriptions. Omitted on `kind=custom`.
- `signals` (array) — The catalog, your custom signals first, then built-ins. Built-ins carry `kind: builtin` plus their taxonomy membership — `category`/`category_label`/`group`/`group_label` (slugs are stable, switch on those; labels are display strings) — yours carry `kind: custom` with the `expr`.

## Status codes

- **200** — Success — the response shape is documented under Returns above.
- **400** — `bad_request` — invalid `kind` value or malformed cursor.

## Sample response

```json
{
  "as_of": "2026-08-11T21:26:00.000Z",
  "count": 421,
  "count_builtin": 421,
  "count_custom": 0,
  "next_cursor": null,
  "taxonomy": {
    "groups": [
      { "slug": "price", "label": "Price", "derivation": "record",
        "description": "Price levels and records: quotes, bars, session levels, and rolling highs & lows...",
        "categories": [
          { "slug": "trades_quotes", "label": "Trades & quotes", "description": "Last trade and the current quote: price, bid/ask, and depth." },
          "... 3 more"
        ] },
      "... 8 more groups (volume, price_action, technicals, fundamentals, events, profile, analyst, news)"
    ]
  },
  "signals": [
    { "name": "above_sma_50", "kind": "builtin", "type": "boolean", "source": "state",
      "description": "Price is above the 50-day simple moving average.",
      "category": "moving_averages", "category_label": "Moving averages & VWAP",
      "group": "technicals", "group_label": "Technicals",
      "cadence": "live_1m_4am_8pm_et",
      "ticker_scope": "all", "history": "full",
      "history_since": "2003-11-18T00:00:00.000Z",
      "resolutions": ["1m", "1h", "1d"], "asset_classes": ["stocks"],
      "asof": "historized", "flag_kind": "state", "firing": "continuous" },
    { "name": "market_cap", "kind": "builtin", "type": "numeric", "source": "state",
      "description": "Market capitalization. Updated daily; intraday change derived from price x shares_outstanding.",
      "category": "valuation", "category_label": "Valuation",
      "group": "fundamentals", "group_label": "Fundamentals",
      "cadence": "post_close_daily",
      "ticker_scope": "all", "history": "full",
      "history_since": "2009-05-19T00:00:00.000Z",
      "resolutions": ["1d"], "asset_classes": ["stocks"],
      "asof": "historized", "unit": "USD" },
    { "name": "rsi_14", "kind": "builtin", "type": "numeric", "source": "state",
      "description": "Relative Strength Index, 14-period. Range 0-100.",
      "category": "oscillators", "category_label": "Oscillators",
      "group": "technicals", "group_label": "Technicals",
      "cadence": "post_close_daily",
      "ticker_scope": "all", "history": "full",
      "history_since": "2003-09-30T00:00:00.000Z",
      "resolutions": ["1d"], "asset_classes": ["stocks"], "asof": "historized" },
    "... (one row per signal)"
  ]
}
```

## Notes

- Built-ins are tagged `kind: 'builtin'` and carry `name`, `type`, `source`; your own are tagged `kind: 'custom'` and carry `name`, `description`, `expr`, `created_at`, `updated_at`. (Rows carried `kind: 'expression'` until 2026-08-15; that spelling is still accepted as a `kind` filter value.)
- Pagination applies only to the custom-signal slice. Built-ins are a small fixed set and come back in their entirety on every page.
- Built-in rows are enriched from the live signal spec: `description`, `category`/`category_label`/`group`/`group_label` (taxonomy membership), `cadence`, `ticker_scope`, `history`, `history_since`, `resolutions`, `asset_classes`, `asof` (whether the signal is historized, i.e. readable via `?asof=`), plus `flag_kind`/`firing`/`definition` (the boolean's firing rule, where authored) on booleans and `unit` where defined. If the spec is briefly unreachable the catalog degrades to bare `name` + `type` + `source` rows rather than erroring.
- Custom signals are private and keyed on `(user_id, name)`. Different users may use the same name for different signals.
- The taxonomy (9 groups → 33 categories, with each level's definition) rides in the response's `taxonomy` block and is rendered with full definitions on the [Signal catalog](/docs/signals/catalog) and its group pages. Slugs are stable identifiers; labels are display strings.

---

Interactive sandbox + parameter editor: https://tickerbot.io/docs/endpoints/signals/list
