Stream
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 row. Each market move arrives as an event: move frame with the same fields as a 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.
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 shows.
When you reconnect, call List Trades with since set to the time of the last frame you received. That fills the gap. See Streaming.
Example request
curl -N "https://api.rivo.markets/v2/stream?channels=trades&venue=kalshi&minUsd=5000" \
-H "Authorization: Bearer $RIVO_API_KEY"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 = Noneconst 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
channelsstringdefaulttrades and movesWhich channels to receive:
trades,moves, or both. Repeat the key to pass both.venuestringReturns rows from these venues:
kalshi,polymarket,polymarket_usorgemini. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.categorystringReturns 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.kindstringReturns 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. To pass several values, repeat the key, up to 50 times. A row matches if it matches any of them.marketmarket refReturns rows for this market. The
refcan 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.priceMinnumberSends 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.
priceMaxnumberSends frames at or below this price, from 0 to 1. It tests the same prices as
priceMin.tradertrader refApplies to trades only. Returns rows for this trader. Take the trader's
reffrom 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.outcomestringApplies to trades only. Returns rows for one side of the market. Pass the side's
name(Yes,No,Lakers,Up) or itslabelfrom Get Market, such asMilwaukee. The match ignores case. You must also pass exactly onemarket.actionstringdefaultbuyApplies to trades only. Choose
buy,sellorall.buyleaves out trims and closes.entrystringdefaultallApplies to trades only. Choose
open,addorall.minUsdnumberApplies to trades only. Sends trades of at least this many dollars.
maxUsdnumberApplies to trades only. Sends trades of at most this many dollars.
namedbooleanApplies to trades only. Set to
trueto receive only trades by a named trader.minWinRatenumberApplies 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.
minSettledintegerApplies to trades only. Sends trades by traders with at least this many settled bets in that kind of market.
typestringApplies to moves only. Choose
move,fliporwhales. Repeat the key to pass several.minPointsnumberApplies to moves only. Sends
moverows 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: tradeframeOne whale trade. Its
datahas the same fields as a List Trades row.Show 13 event: trade fields
idstringThe trade's id. It never changes, so the same trade has the same id in every listing and on the stream.
timestringWhen the trade happened on the venue, in ISO 8601 UTC.
marketobjectThe market this row is about.
Show 6 market fields
refmarket refThe market's ref. You can pass it to any
marketfilter or to Get Market. On a market of several outcomes, it names the whole event.contractmarket ref or nullThe ref of the exact contract. It is the same as
refon 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 or a market bot.titlestringThe market's title, as the venue writes it.
venuestringThe venue that lists the market: one of
kalshi,polymarket,polymarket_usorgemini.categorystring or nullThe market's category: one of
sports,politics,crypto,finance,culture. It isnullwhen Rivo has not classified the market.kindstring or nullThe kind of market, such as
nflorbitcoin. It isnullwhen Rivo has not classified the market yet.
outcomestringThe side the trader bought or sold, by its
name. On Polymarket it is the outcome's own name, such asLakers. On every other venue it isYesorNo.actionstringWhether the trader bought or sold:
buyorsell.entrystring or nullWhat the trade did to the trader's position:
open,add,flip,trimorclose. It isnullwhen the venue does not say.pricenumber or nullThe price paid for each contract, from 0 to 1.
usdnumber or nullThe dollars traded.
contractsnumber or nullThe number of contracts traded.
traderobject or nullThe 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
kalshiorpolymarketname their traders. On every other venue this isnull.Show 5 trader fields
reftrader refThe trader's ref. You can pass it to any
traderfilter or to Get Trader.namestring or nullThe trader's public name, or
nullwhen they have none. The API never returns a wallet address as a name.winRatenumber or nullThe 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
nulluntil they have 10 settled bets.profitnumber or nullThe trader's profit in USD, over the same bets as
winRate.settledintegerThe number of settled bets behind
winRateandprofit.
agreementobjectThe other whale trades on this market in the 24 hours before this one.
Show 2 agreement fields
sameSideobjectTrades that bought the same side.
Show 2 sameSide fields
tradesintegerThe number of whale trades.
usdnumberThe dollars those trades spent.
otherSideobjectTrades that bought the other side.
Show 2 otherSide fields
tradesintegerThe number of whale trades.
usdnumberThe dollars those trades spent.
resultstringThe trade's result. It is
openuntil the market settles, thenwin,lossorvoid.profitnumber or nullWhat the trade made in USD, if held to settlement at the price paid. It is
nullwhile the market is open.
event: moveframeOne market move. Its
datahas the same fields as a List Moves row.Show 10 event: move fields
idstringThe move's id. It never changes, so the listing and the stream use the same id.
timestringWhen Rivo recorded the move, in ISO 8601 UTC.
typestringWhat happened.
movemeans the Yes price moved sharply.flipmeans the favorite changed.whalesmeans several whales bought one side within minutes.marketobjectThe market this row is about.
Show 6 market fields
refmarket refThe market's ref. You can pass it to any
marketfilter or to Get Market. On a market of several outcomes, it names the whole event.contractmarket ref or nullThe ref of the exact contract. It is the same as
refon 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 or a market bot.titlestringThe market's title, as the venue writes it.
venuestringThe venue that lists the market: one of
kalshi,polymarket,polymarket_usorgemini.categorystring or nullThe market's category: one of
sports,politics,crypto,finance,culture. It isnullwhen Rivo has not classified the market.kindstring or nullThe kind of market, such as
nflorbitcoin. It isnullwhen Rivo has not classified the market yet.
outcomestringThe side the move is about. On a
moveit isYes, the side whose price moved. On aflipit is the new favorite. Onwhalesit is the side the whales bought.priceBeforenumber or nullOn a
move, the Yes price before the move. On other types it isnull.priceAfternumber or nullThe Yes price when Rivo recorded the move. It is
nullon awhalesmove when Rivo has no price yet.minutesinteger or nullOn a
move, the number of minutes the move took. On other types it isnull.tradersinteger or nullOn
whales, the number of whales who bought. On other types it isnull.usdnumber or nullOn
whales, the dollars those whales spent between them. On other types it isnull.
event: errorframeThe server sends this just before it closes the stream, because the day's trade allowance ran out.
Show 2 event: error fields
codestringAlways
QUOTA_EXCEEDED.errorstringA sentence that says what happened and when the allowance resets.
: connectedcommentThe server sends this once when the stream opens.
: pingcommentThe server sends this heartbeat every 25 seconds. You can ignore it.
Limits
- A key can hold at most 3 streams at once, and
/v1/streamcounts 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: errorand 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. |