# Stream

> Pushes trades and market moves to you over one server-sent events connection, with the same filters as the listings.

Source: https://rivo.markets/docs/reference/stream

`GET /v2/stream`

Scope: `read`

This route opens a server-sent events stream. Each new whale trade arrives as an `event: trade` frame with the same fields as a [List Trades](https://rivo.markets/docs/reference/list-trades) row. Each market move arrives as an `event: move` frame with the same fields as a [List Moves](https://rivo.markets/docs/reference/list-moves) row. Each connection has its own filters, and they have the same names and meanings as on the two listings.

`market` narrows both channels. So `?market=<ref>` sends you every trade and every move on one market over a single connection.

> **Note:** Send the key in the `Authorization` header. The stream does not accept the key in the URL. To show the stream in a browser, open it on your server and pass the frames on, as [Streaming to a browser](https://rivo.markets/docs/authentication#streaming-to-a-browser) shows.

When you reconnect, call [List Trades](https://rivo.markets/docs/reference/list-trades) with `since` set to the time of the last frame you received. That fills the gap. See [Streaming](https://rivo.markets/docs/streaming).

## Example request

curl:

```bash
curl -N "https://api.rivo.markets/v2/stream?channels=trades&venue=kalshi&minUsd=5000" \
  -H "Authorization: Bearer $RIVO_API_KEY"
```

Python:

```python
import json
import os
import requests

url = "https://api.rivo.markets/v2/stream"
params = {"channels": "trades", "venue": "kalshi", "minUsd": 5000}
headers = {"Authorization": f"Bearer {os.environ['RIVO_API_KEY']}"}

with requests.get(url, params=params, headers=headers, stream=True, timeout=(10, None)) as res:
    res.raise_for_status()
    event = None
    for line in res.iter_lines(decode_unicode=True):
        if line.startswith("event: "):
            event = line[len("event: "):]
        elif line.startswith("data: "):
            frame = json.loads(line[len("data: "):])
            if event == "error":
                raise RuntimeError(frame["error"])
            print(event, frame["market"]["title"], frame.get("usd"))
        elif line == "":
            event = None
```

TypeScript:

```typescript
const url = new URL("https://api.rivo.markets/v2/stream");
url.searchParams.set("channels", "trades");
url.searchParams.set("venue", "kalshi");
url.searchParams.set("minUsd", "5000");

const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.RIVO_API_KEY}` },
});
if (!res.ok || !res.body) throw new Error(`stream refused: ${res.status}`);

const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  let end: number;
  while ((end = buffer.indexOf("\n\n")) !== -1) {
    const frame = buffer.slice(0, end);
    buffer = buffer.slice(end + 2);
    const event = /^event: (.+)$/m.exec(frame)?.[1];
    const data = /^data: (.+)$/m.exec(frame)?.[1];
    if (!event || !data) continue; // a ": ping" heartbeat
    console.log(event, JSON.parse(data));
  }
}
```

## Query parameters

- `channels` (string, optional, default `trades and moves`): Which channels to receive: `trades`, `moves`, or both. Repeat the key to pass both.
- `venue` (string, optional): Returns rows from these venues: `kalshi`, `polymarket`, `polymarket_us` or `gemini`. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.
- `category` (string, optional): Returns rows in these categories: `sports`, `politics`, `crypto`, `finance`, `culture`. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.
- `kind` (string, optional): Returns rows in these kinds of market. Each kind belongs to one category: `nfl`, `college_football`, `nba`, `wnba`, `college_basketball`, `basketball`, `mlb`, `nhl`, `soccer`, `tennis`, `golf`, `mma`, `boxing`, `motorsport`, `cricket`, `esports`, `chess`, `rugby`, `olympics`, `parlay`, `other_sports`, `us_elections`, `us_politics`, `world_politics`, `other_politics`, `bitcoin`, `ethereum`, `solana`, `xrp`, `other_crypto`, `fed_rates`, `economic_data`, `stocks_indices`, `companies`, `commodities`, `fx`, `other_finance`, `mentions`, `weather`, `entertainment`, `tech_ai`, `health`, `other_culture`. See [Kinds of market](https://rivo.markets/docs/concepts#kinds-of-market). To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.
- `market` (market ref, optional): Returns rows for this market. The `ref` can name one contract or a market with several outcomes. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them. It narrows both channels.
- `priceMin` (number, optional): Sends frames at or above this price, from 0 to 1. On a trade it tests the price paid. On a move it tests the Yes price after the move.
- `priceMax` (number, optional): Sends frames at or below this price, from 0 to 1. It tests the same prices as `priceMin`.
- `trader` (trader ref, optional): Applies to trades only. Returns rows for this trader. Take the trader's `ref` from any row that names them. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.
- `outcome` (string, optional): Applies to trades only. Returns rows for one side of the market. Pass the side's `name` (`Yes`, `No`, `Lakers`, `Up`) or its `label` from [Get Market](https://rivo.markets/docs/reference/get-market), such as `Milwaukee`. The match ignores case. You must also pass exactly one `market`.
- `action` (string, optional, default `buy`): Applies to trades only. Choose `buy`, `sell` or `all`. `buy` leaves out trims and closes.
- `entry` (string, optional, default `all`): Applies to trades only. Choose `open`, `add` or `all`.
- `minUsd` (number, optional): Applies to trades only. Sends trades of at least this many dollars.
- `maxUsd` (number, optional): Applies to trades only. Sends trades of at most this many dollars.
- `named` (boolean, optional): Applies to trades only. Set to `true` to receive only trades by a named trader.
- `minWinRate` (number, optional): Applies to trades only. Sends trades by traders whose win rate in that kind of market is at least this, from 0 to 1, over at least 10 settled bets.
- `minSettled` (integer, optional): Applies to trades only. Sends trades by traders with at least this many settled bets in that kind of market.
- `type` (string, optional): Applies to moves only. Choose `move`, `flip` or `whales`. Repeat the key to pass several.
- `minPoints` (number, optional): Applies to moves only. Sends `move` rows whose price changed by at least this many points, from 0 to 100.

## Example response

```
: connected

event: trade
data: {"id":"1ce3724a-941e-5b4a-8e84-f2e50ae89bb8","time":"2026-10-15T02:24:53.011Z","market":{"ref":"a2Fsc2hpOm1hcmtldDpLWE1MQkdBTUUtMjZPQ1QxNDIwMDBNSUxMQUQtTUlM","title":"Milwaukee wins","venue":"kalshi","category":"sports","kind":"mlb"},"outcome":"Yes","action":"buy","entry":null,"price":0.88,"usd":7933.36,"contracts":9015.18,"trader":{"ref":"kalshi:scottramirez","name":"scottramirez","winRate":0.8269,"profit":5122.43,"settled":52},"agreement":{"sameSide":{"trades":37,"usd":95502.12},"otherSide":{"trades":0,"usd":0}},"result":"open","profit":null}

event: move
data: {"id":"flip:kalshi:market:KXBTCD-26OCT1423-T80799.99:1792031312711","time":"2026-10-15T02:28:32.711Z","type":"flip","market":{"ref":"a2Fsc2hpOm1hcmtldDpLWEJUQ0QtMjZPQ1QxNDIzLVQ4MDc5OS45OQ","title":"Bitcoin price on Oct 14, 2026?","venue":"kalshi","category":"crypto","kind":"bitcoin"},"outcome":"No","priceBefore":null,"priceAfter":0.36,"minutes":null,"traders":null,"usd":null}

: ping

```

## Response fields

- `event: trade` (frame): One whale trade. Its `data` has the same fields as a [List Trades](https://rivo.markets/docs/reference/list-trades) row.
  - `id` (string): The trade's id. It never changes, so the same trade has the same id in every listing and on the stream.
  - `time` (string): When the trade happened on the venue, in ISO 8601 UTC.
  - `market` (object): The market this row is about.
    - `ref` (market ref): The market's ref. You can pass it to any `market` filter or to [Get Market](https://rivo.markets/docs/reference/get-market). On a market of several outcomes, it names the whole event.
    - `contract` (market ref or null): The ref of the exact contract. It is the same as `ref` on a market with one contract. On a market of several outcomes, such as a game with a moneyline and a spread, it tells the contracts apart. Pass it to [Place Paper Order](https://rivo.markets/docs/reference/place-paper-order) or a market bot.
    - `title` (string): The market's title, as the venue writes it.
    - `venue` (string): The venue that lists the market: one of `kalshi`, `polymarket`, `polymarket_us` or `gemini`.
    - `category` (string or null): The market's category: one of `sports`, `politics`, `crypto`, `finance`, `culture`. It is `null` when Rivo has not classified the market.
    - `kind` (string or null): The kind of market, such as `nfl` or `bitcoin`. It is `null` when Rivo has not classified the market yet.
  - `outcome` (string): The side the trader bought or sold, by its `name`. On Polymarket it is the outcome's own name, such as `Lakers`. On every other venue it is `Yes` or `No`.
  - `action` (string): Whether the trader bought or sold: `buy` or `sell`.
  - `entry` (string or null): What the trade did to the trader's position: `open`, `add`, `flip`, `trim` or `close`. It is `null` when the venue does not say.
  - `price` (number or null): The price paid for each contract, from 0 to 1.
  - `usd` (number or null): The dollars traded.
  - `contracts` (number or null): The number of contracts traded.
  - `trader` (object or null): The trader who made the trade, with their record in this kind of market, or their whole record when Rivo has none for that kind. Only `kalshi` or `polymarket` name their traders. On every other venue this is `null`.
    - `ref` (trader ref): The trader's ref. You can pass it to any `trader` filter or to [Get Trader](https://rivo.markets/docs/reference/get-trader).
    - `name` (string or null): The trader's public name, or `null` when they have none. The API never returns a wallet address as a name.
    - `winRate` (number or null): The share of their settled bets the trader won, from 0 to 1. It covers the kind of market this row is about, or their whole record when Rivo has none for that kind. It is `null` until they have 10 settled bets.
    - `profit` (number or null): The trader's profit in USD, over the same bets as `winRate`.
    - `settled` (integer): The number of settled bets behind `winRate` and `profit`.
  - `agreement` (object): The other whale trades on this market in the 24 hours before this one.
    - `sameSide` (object): Trades that bought the same side.
      - `trades` (integer): The number of whale trades.
      - `usd` (number): The dollars those trades spent.
    - `otherSide` (object): Trades that bought the other side.
      - `trades` (integer): The number of whale trades.
      - `usd` (number): The dollars those trades spent.
  - `result` (string): The trade's result. It is `open` until the market settles, then `win`, `loss` or `void`.
  - `profit` (number or null): What the trade made in USD, if held to settlement at the price paid. It is `null` while the market is open.
- `event: move` (frame): One market move. Its `data` has the same fields as a [List Moves](https://rivo.markets/docs/reference/list-moves) row.
  - `id` (string): The move's id. It never changes, so the listing and the stream use the same id.
  - `time` (string): When Rivo recorded the move, in ISO 8601 UTC.
  - `type` (string): What happened. `move` means the Yes price moved sharply. `flip` means the favorite changed. `whales` means several whales bought one side within minutes.
  - `market` (object): The market this row is about.
    - `ref` (market ref): The market's ref. You can pass it to any `market` filter or to [Get Market](https://rivo.markets/docs/reference/get-market). On a market of several outcomes, it names the whole event.
    - `contract` (market ref or null): The ref of the exact contract. It is the same as `ref` on a market with one contract. On a market of several outcomes, such as a game with a moneyline and a spread, it tells the contracts apart. Pass it to [Place Paper Order](https://rivo.markets/docs/reference/place-paper-order) or a market bot.
    - `title` (string): The market's title, as the venue writes it.
    - `venue` (string): The venue that lists the market: one of `kalshi`, `polymarket`, `polymarket_us` or `gemini`.
    - `category` (string or null): The market's category: one of `sports`, `politics`, `crypto`, `finance`, `culture`. It is `null` when Rivo has not classified the market.
    - `kind` (string or null): The kind of market, such as `nfl` or `bitcoin`. It is `null` when Rivo has not classified the market yet.
  - `outcome` (string): The side the move is about. On a `move` it is `Yes`, the side whose price moved. On a `flip` it is the new favorite. On `whales` it is the side the whales bought.
  - `priceBefore` (number or null): On a `move`, the Yes price before the move. On other types it is `null`.
  - `priceAfter` (number or null): The Yes price when Rivo recorded the move. It is `null` on a `whales` move when Rivo has no price yet.
  - `minutes` (integer or null): On a `move`, the number of minutes the move took. On other types it is `null`.
  - `traders` (integer or null): On `whales`, the number of whales who bought. On other types it is `null`.
  - `usd` (number or null): On `whales`, the dollars those whales spent between them. On other types it is `null`.
- `event: error` (frame): The server sends this just before it closes the stream, because the day's trade allowance ran out.
  - `code` (string): Always `QUOTA_EXCEEDED`.
  - `error` (string): A sentence that says what happened and when the allowance resets.
- `: connected` (comment): The server sends this once when the stream opens.
- `: ping` (comment): The server sends this heartbeat every 25 seconds. You can ignore it.

## Limits

- A key can hold at most 3 streams at once, and `/v1/stream` counts toward the same 3.
- An open stream uses one of your 120 requests every minute.
- Every trade frame the server sends counts toward your daily allowance of 50,000 trades. Rivo charges them once a minute. A trade that a filter leaves out is not sent and does not count. Move frames do not count.
- When the allowance runs out, the stream sends `event: error` and closes. The API refuses new streams until the allowance resets.

## Errors

If the request fails, the API returns an ordinary JSON body and status before the stream opens.

| Code | When |
| --- | --- |
| `UNAUTHORIZED` | You sent no key in the `Authorization` header, or the key is wrong or revoked. |
| `SUBSCRIPTION_INACTIVE` | The account that owns the key has no active subscription. |
| `VALIDATION` | A parameter is out of range or unknown, you sent `outcome` without exactly one `market`, or you put the key in the URL as `?key=`. |
| `QUOTA_EXCEEDED` | The key already holds 3 streams, or you used up today's trade allowance. |
