# GET /v2/events

**List events**

The unified cross-ticker event stream — "all splits this month", "every analyst action in my universe this week" — one call instead of one per ticker. Four kinds, each backed by its own archive: `dividend`, `split`, `insider`, `analyst`. Rows are `{ ticker, ts, kind, payload }`, newest first; payload fields per kind are identical to [`/v2/tickers/{ticker}/events`](/docs/endpoints/tickers/events) (corporate kinds) and [`/v2/analyst/events`](/docs/endpoints/analyst/events) (analyst), so consumers can switch scope without reshaping.

Those two endpoints remain as the single-ticker and analyst-specialized (firm/action filters) views of the same data.

**The endpoint speaks the SQL grammar** over a normalized projection of exactly four columns — `ticker`, `ts`, `kind`, and `payload` (jsonb). Optional `q` filters rows (`kind='analyst' AND payload->>'action'='downgrades'`); `group_by` (with optional `select`/`having`) switches to aggregate rollups (`group_by=payload->>'firm' AS firm`). Payload fields are reached with jsonb operators and cast as needed: `(payload->>'shares')::numeric > 1e6`. No other identifiers are accepted — this is the event log's own grammar, not the ticker-state scan grammar. Aggregates don't paginate: past `limit` you get `truncated: true` — narrow the window instead.

Requires at least one bound — a ticker scope or a time window (`q` alone does not count); a bare `kind` over millions of rows is refused. Both `GET` and `POST` are supported; `POST` takes the same parameters in the body for long queries. Pagination note: the cursor pages strictly *older* than the last row's timestamp, and the corporate archives are date-grained — same-date remainders can fall on a page boundary. Raise `limit` (max 1000) to avoid boundaries. A `q` longer than 600 chars is not carried inside the cursor — resend `q` alongside `cursor` on later pages.

## Plan access

- **Plan access.** Available on every plan, alongside the live `last_rating_date` / `last_rating_firm` / `last_rating_action` columns on the ticker object and the `recent_*` analyst flags on /v2/scan.
- **Rate limit.** Hobby 600/min · Pro 6,000/min · Scale 60,000/min.
- **Per-call cap.** 1000 rows per call on every plan. History back to 2012.

## Query / path parameters

| Name | In | Type | Required | Description |
|------|----|----|----------|-------------|
| `kind` | query | string | no | Comma list of kinds to include. Enum: `dividend`, `split`, `insider`, `analyst`. Example: `split,analyst`. |
| `ticker` | query | string | no | Single-ticker filter. Example: `AAPL`. |
| `tickers` | query | string | no | Comma list of tickers (max 50). Mutually exclusive with `universe`. Example: `AAPL,NVDA,TSLA`. |
| `universe` | query | string | no | Universe slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`. |
| `since` | query | string | no | Events at/after this ISO date or datetime. Example: `2026-07-01`. |
| `until` | query | string | no | Events strictly before this ISO date or datetime. |
| `q` | query | string | no | SQL WHERE over the projection — only `ticker`, `ts`, `kind`, `payload` may appear. Max 4000 chars. ANDs with the filter params. Example: `kind='analyst' AND payload->>'action'='downgrades'`. |
| `select` | query | string | no | Aggregate-mode output columns (requires `group_by`). Default: group keys + `COUNT(*) AS events`. |
| `group_by` | query | string | no | Comma list of rollup keys — switches the response to aggregate rows. Example: `payload->>'firm' AS firm`. |
| `having` | query | string | no | Post-aggregation filter. Requires `group_by`. Example: `COUNT(*) > 5`. |
| `order` | query | string | no | Aggregate-mode sort column or alias. Default: `events`. (Row mode is always newest-first.) |
| `dir` | query | string | no | Aggregate-mode sort direction. Enum: `asc`, `desc`. Default: `desc`. |
| `limit` | query | integer | no | Page size (row modes) / max rollup rows (aggregate mode). Max 1000. Default: `50`. |
| `cursor` | query | string | no | Opaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`. |

## Status codes

- **200** — Envelope with `query`, `count`, `next_cursor`, and `results` of `{ ticker, ts, kind, payload }` (row modes) or rollup rows plus `truncated: true` when the aggregate exceeds `limit`.
- **400** — Bad `kind`, >50 tickers, `tickers`+`universe` together, no bound at all, invalid timestamps/cursor, or `invalid_query` — an identifier outside the four projection columns, a disallowed function, `select`/`having`/`order` without `group_by`, or `cursor` with `group_by`.
- **404** — Referenced `universe` does not exist.

## Sample response

```json
{
  "as_of": "2026-07-27T15:58:12.000Z",
  "query": { "kind": "split,analyst", "tickers": null, "universe": "top_100", "since": "2026-07-01T00:00:00.000Z", "until": null, "limit": 50 },
  "count": 3,
  "next_cursor": null,
  "results": [
    { "ticker": "AAPL", "ts": "2026-07-26T14:30:00.000Z", "kind": "analyst",
      "payload": { "event_id": "ev_9f2", "firm": "Morgan Stanley", "analyst": "K. Huberty", "action": "downgrades", "rating": "Equal-Weight", "previous_rating": "Overweight", "price_target": 210, "previous_price_target": 250, "price_target_action": "lowers", "importance": 3 } },
    { "ticker": "NVDA", "ts": "2026-07-22T00:00:00.000Z", "kind": "split",
      "payload": { "split_from": 1, "split_to": 10, "ratio": 0.1 } },
    "(...)"
  ]
}
```

## Examples

### Splits and analyst actions across the top 100, this month

Request:

```shell
curl "https://api.tickerbot.io/v2/events?universe=top_100&kind=split,analyst&since=2026-07-01" \
  -H "Authorization: Bearer YOUR_KEY"
```

Response (`200`):

```json
{
  "as_of": "2026-07-27T15:58:12.000Z",
  "query": { "kind": "split,analyst", "tickers": null, "universe": "top_100", "since": "2026-07-01T00:00:00.000Z", "until": null, "limit": 50 },
  "count": 3,
  "next_cursor": null,
  "results": [
    { "ticker": "AAPL", "ts": "2026-07-26T14:30:00.000Z", "kind": "analyst",
      "payload": { "event_id": "ev_9f2", "firm": "Morgan Stanley", "analyst": "K. Huberty", "action": "downgrades", "rating": "Equal-Weight", "previous_rating": "Overweight", "price_target": 210, "previous_price_target": 250, "price_target_action": "lowers", "importance": 3 } },
    { "ticker": "NVDA", "ts": "2026-07-22T00:00:00.000Z", "kind": "split",
      "payload": { "split_from": 1, "split_to": 10, "ratio": 0.1 } },
    "(...)"
  ]
}
```

### Downgrade volume by firm, this month (aggregate mode)

Request:

```shell
curl -G "https://api.tickerbot.io/v2/events" \
  -H "Authorization: Bearer YOUR_KEY" \
  --data-urlencode "kind=analyst" \
  --data-urlencode "since=2026-07-01" \
  --data-urlencode "q=payload->>'action'='downgrades'" \
  --data-urlencode "group_by=payload->>'firm' AS firm" \
  --data-urlencode "having=COUNT(*) > 5"
```

Response (`200`):

```json
{
  "as_of": "2026-07-28T13:05:00.000Z",
  "query": { "kind": "analyst", "since": "2026-07-01T00:00:00.000Z", "q": "payload->>'action'='downgrades'", "group_by": ["payload->>'firm' AS firm"], "having": "COUNT(*) > 5", "order": "events", "dir": "desc", "limit": 50 },
  "count": 2,
  "results": [
    { "firm": "Morgan Stanley", "events": 14 },
    { "firm": "Goldman Sachs", "events": 9 }
  ]
}
```

---

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