API Reference
Plug live signals into your own stack. The API is read-only: it serves market data and can never place an order or touch an exchange account. Part of VIP Quant. New to the terminal? Start with the docs.
Base URL:
https://api.arbitragex.io
Authentication
Create a key under Settings. It is shown once and stored only as a hash, so keep it safe and never put it in a browser. Lost keys can't be recovered: revoke and create another. Up to 5 keys per user. Send it in the X-API-Key header on every request, including the websocket upgrade.
curl -H "X-API-Key: $ARBITRAGEX_KEY" https://api.arbitragex.io/v1/opportunities
import os, requests
rows = requests.get(
"https://api.arbitragex.io/v1/opportunities",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=5,
).json()["rows"]
for row in rows[:5]:
print(row["coin"], row["buy"]["venue"], row["sell"]["venue"], row["netUsd"]["1000"])const res = await fetch("https://api.arbitragex.io/v1/opportunities", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
const { rows } = await res.json();
for (const row of rows.slice(0, 5)) {
console.log(row.coin, row.buy.venue, row.sell.venue, row.netUsd["1000"]);
}A revoked key stops working within seconds, and open sockets close soon after. A key stops when its plan does, and starts again if the plan comes back.
Rate limits
Each key has its own limit, 120 requests a minute by default, counted in one-minute windows, plus 3 open sockets. Every response tells you where you stand:
X-RateLimit-Limit: requests allowed per window.X-RateLimit-Remaining: requests left in this window.X-RateLimit-Reset: seconds until the window ends.
Prefer the websocket for live rows. It pushes changes as they happen, and one socket does not count against the limit after it opens.
Errors
Errors are JSON: { "error": "Rate limit reached." }
- 401: the key is wrong, revoked, or its plan has ended.
- 405: the API only accepts GET.
- 429: rate limit or socket limit reached. Wait for
Retry-Afterseconds. - 503: keys can't be checked right now. Retry shortly.
OpenAPI
The whole API is described in one OpenAPI 3.1 document at a stable address. Import it into Postman, Insomnia or Swagger UI, or generate a typed client from it. Every endpoint in it carries the curl, Python and JavaScript examples below.
https://arbitragex.io/openapi.json
# A typed client, for example: npx openapi-typescript https://arbitragex.io/openapi.json -o arbitragex.d.ts openapi-python-client generate --url https://arbitragex.io/openapi.json
Endpoint reference
Every REST endpoint, generated from the OpenAPI document. Each example reads your key from the ARBITRAGEX_KEY environment variable.
- /v1/opportunities Live opportunities
- /v1/listings New listings
- /v1/notices Exchange notices
- /v1/funding Funding rates
- /v1/funding/history Funding history
- /v1/premiums Regional premiums
- /v1/markets Volume and open interest
- /v1/fees Taker fees
- /v1/reliability Venue reliability
- /v1/spread/{id} A route's spread over time
- /v1/proof Proof Log
- /v1/events Prediction market routes
- /v1/events/proof Prediction market Proof Log
- /v1/events/markets Prediction market browser
- /v1/events/history A market's odds over time
- /v1/events/holders A Polymarket market's holders and big bets
get/v1/opportunitiesLive opportunities
Every route on the feed right now, as the plan sees it. Reading it often is fine; the stream is better past a few times a minute.
strategyquery: `all`, the default, or one trade type.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/opportunities"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/opportunities",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["coin"], row["buy"]["venue"], row["sell"]["venue"], row["netBps"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/opportunities", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.coin, row.buy.venue, row.sell.venue, row.netBps);get/v1/listingsNew listings
Markets first seen in the last 72 hours, newest first, and listings exchanges have announced but don't list yet.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/listings"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/listings",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"]:
print(row["venue"], row["kind"], row["coin"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/listings", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows) console.log(row.venue, row.kind, row.coin);get/v1/noticesExchange notices
Delistings, deposit and withdrawal suspensions, maintenance and announced listings still in effect, newest first. The same for every plan.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/notices"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/notices",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"]:
print(row["venue"], row["kind"], row["coins"], row["title"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/notices", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows) console.log(row.venue, row.kind, row.coins, row.title);get/v1/fundingFunding rates
Every perp's current funding rate, per interval and annualised.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/funding"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/funding",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
top = sorted(data["rows"], key=lambda r: -abs(r["aprPct"]))[:5]
for row in top:
print(row["coin"], row["venue"], row["aprPct"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/funding", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
const top = [...data.rows].sort((a, b) => Math.abs(b.aprPct) - Math.abs(a.aprPct)).slice(0, 5);
for (const row of top) console.log(row.coin, row.venue, row.aprPct);get/v1/funding/historyFunding history
One coin's funding settlements over the last 30 days, oldest first. A gap is never filled in.
coinquery, required: The coin, in any case.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/funding/history?coin=BTC"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/funding/history?coin=BTC",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"]:
print(row["venue"], len(row["points"]), "settlements")JavaScript
const res = await fetch("https://api.arbitragex.io/v1/funding/history?coin=BTC", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows) console.log(row.venue, row.points.length, "settlements");get/v1/premiumsRegional premiums
Each coin's premium in each local currency over the middle USDT price on order book venues, priced through each venue's own USDT book, with each currency's index and its 24 hours of 5-minute means. With coin, that coin's rows only, each with its own 24 hours.
coinquery: One coin, in any case.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/premiums"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/premiums",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["coin"], row["fiat"], row["premiumBps"], row["verified"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/premiums", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.coin, row.fiat, row.premiumBps, row.verified);get/v1/marketsVolume and open interest
One row per market, busiest first, as arrays: [venue, kind, coin, symbol, volumeUsd, oiUsd]. Null where the venue publishes no figure.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/markets"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/markets",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for venue, kind, coin, symbol, volume, oi in data["rows"][:5]:
print(venue, kind, symbol, volume, oi)JavaScript
const res = await fetch("https://api.arbitragex.io/v1/markets", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const [venue, kind, , symbol, volume, oi] of data.rows.slice(0, 5)) console.log(venue, kind, symbol, volume, oi);get/v1/feesTaker fees
Each venue's published base taker fee, the rate net figures are priced at. Re-price a route at your own fees with the formula on /docs/api#fees.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/fees"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/fees",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"]:
print(row["venue"], row["spotTakerBps"], row["perpTakerBps"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/fees", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows) console.log(row.venue, row.spotTakerBps, row.perpTakerBps);get/v1/reliabilityVenue reliability
Each venue's track record over the last 7 and 30 UTC days, from watched time only: uptime, outages, how fresh its prices were, the share of its routes that lasted 30 seconds, and wallet list changes. A figure with too little behind it is null, never a guess.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/reliability"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/reliability",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["venue"], row["online"], row["uptime7d"], row["heldShare30d"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/reliability", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.venue, row.online, row.uptime7d, row.heldShare30d);get/v1/spread/{id}A route's spread over time
Net bps over time for one route. Without a range, the latest row, up to 72 points. With `from` or `to`, every row in that stretch, at most 2,000 points, keeping the peaks.
idpath, required: A route id, URL-encoded.fromquery: Earliest point, unix ms.toquery: Latest point, unix ms.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/spread/BTC%7CBinance%3Aspot%7COKX%3Aperp"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/spread/BTC%7CBinance%3Aspot%7COKX%3Aperp",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
print(len(data["points"]), "points, peak", max((p["netBps"] for p in data["points"]), default=None))JavaScript
const res = await fetch("https://api.arbitragex.io/v1/spread/BTC%7CBinance%3Aspot%7COKX%3Aperp", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
console.log(data.points.length, "points, peak", Math.max(...data.points.map((p) => p.netBps)));get/v1/proofProof Log
Closed opportunities that lasted at least 30 seconds, newest first, with a cursor for the next page.
limitquery: Rows per page, 50 by default, at most 500.fromquery: Earliest close, unix ms.toquery: Latest close, unix ms.coinquery: One coin, exact in any case.cursorquery: `next` from the previous page.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/proof?limit=100"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/proof?limit=100",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["coin"], row["buyVenue"], row["sellVenue"], row["peakNetBps"])
print("next page:", data["next"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/proof?limit=100", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.coin, row.buyVenue, row.sellVenue, row.peakNetBps);
console.log("next page:", data.next);get/v1/eventsPrediction market routes
Sets of contracts on Polymarket, Polymarket US and Kalshi that cost less after fees than the $1 they pay at settlement, best net first, as the plan sees them. Every `cross` route is Caution: each exchange settles by its own rules, attached in `rules`.
kindquery: `all`, the default, or one route kind.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/events"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/events",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["kind"], row["title"], row["netBps"], row["days"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/events", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.kind, row.title, row.netBps, row.days);get/v1/events/proofPrediction market Proof Log
Prediction market routes that ended after lasting at least 30 seconds, newest first, with a cursor for the next page. Kept a year.
limitquery: Rows per page, 50 by default, at most 500.fromquery: Earliest close, unix ms.toquery: Latest close, unix ms.kindquery: `all`, the default, or one route kind.cursorquery: `next` from the previous page.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/events/proof?limit=100"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/events/proof?limit=100",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["kind"], row["title"], row["venues"], row["peakNetBps"])
print("next page:", data["next"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/events/proof?limit=100", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.kind, row.title, row.venues, row.peakNetBps);
console.log("next page:", data.next);get/v1/events/marketsPrediction market browser
Every open market on Polymarket, Polymarket US and Kalshi, routes or not, joined into events so each venue's odds for the same outcome stand side by side. Odds are YES's probability, 0 to 1. With `group`, one event whole; with `markets`, the events holding those markets. Live on every plan.
qquery: Every word in the title, outcomes or questions. At most 100 characters.categoryquery: One category.venuequery: Events on this venue.daysquery: An outcome resolving within this many days.sortquery: `volume`, the default, `closing` or `movers`.limitquery: Rows per page, 25 by default, at most 50.cursorquery: `next` from the previous page.groupquery: One event's id, for all its outcomes.marketsquery: Up to 50 `<venue>|<market id>`, comma separated.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/events/markets?category=crypto&sort=closing"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/events/markets?category=crypto&sort=closing",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for row in data["rows"][:5]:
print(row["title"], [(q["venue"], q["prob"]) for q in row["lines"][0]["quotes"]])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/events/markets?category=crypto&sort=closing", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const row of data.rows.slice(0, 5)) console.log(row.title, row.lines[0].quotes.map((q) => [q.venue, q.prob]));get/v1/events/historyA market's odds over time
YES's probability over a day, week or month, oldest first: from the venue where it keeps a history, else sampled by us.
venuequery, required: The market's venue.marketquery, required: The market id, as in a quote's `marketId`.rangequery: `1d`, `1w` or `1m`.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/events/history"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/events/history",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
print(len(data["points"]), "points from", data["source"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/events/history", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
console.log(data.points.length, "points from", data.source);get/v1/events/holdersA Polymarket market's holders and big bets
The top 10 holders of each outcome and the latest 20 bets of $1,000 or more, as Polymarket's public data states them.
marketquery, required: Polymarket's condition id.
curl
curl -s -H "X-API-Key: $ARBITRAGEX_KEY" "https://api.arbitragex.io/v1/events/holders"
Python
import os
import requests
res = requests.get(
"https://api.arbitragex.io/v1/events/holders",
headers={"X-API-Key": os.environ["ARBITRAGEX_KEY"]},
timeout=10,
)
res.raise_for_status()
data = res.json()
for h in data["holders"][:5]:
print(h["side"], h["name"] or h["address"], h["shares"])JavaScript
const res = await fetch("https://api.arbitragex.io/v1/events/holders", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
if (!res.ok) throw new Error((await res.json()).error);
const data = await res.json();
for (const h of data.holders.slice(0, 5)) console.log(h.side, h.name ?? h.address, h.shares);Opportunities
GET /v1/opportunities returns every live row, highest net first.
{
"rows": [
{
"id": "BTC|Binance:spot|OKX:perp",
"coin": "BTC",
"strategy": "spot_perp",
"buy": { "venue": "Binance", "kind": "spot", "symbol": "BTCUSDT", "price": 68250.1,
"sizeUsd": 41200, "ageMs": 120, "fundingBps8h": null, "url": "https://..." },
"sell": { "venue": "OKX", "kind": "perp", "symbol": "BTC-USDT-SWAP", "price": 68310.4,
"sizeUsd": 38900, "ageMs": 240, "fundingBps8h": 1.2, "url": "https://..." },
"grossBps": 8.8,
"netBps": 2.1,
"netUsd": { "100": 0.2, "1000": 2.1, "10000": 19.4, "100000": null, "1000000": null },
"maxSizeUsd": 21400,
"exitBps": 6.2,
"transfer": "not_needed",
"network": null,
"status": "verified",
"reasons": [],
"firstSeenMs": 1793570209670,
"updatedMs": 1793570229670,
"spark": [2.0, 2.1]
}
],
"delayedMs": 0
}Funding
GET /v1/funding returns the current funding rate of every perp.
{ "rows": [ { "coin": "BTC", "venue": "Binance", "symbol": "BTCUSDT", "ratePct": 0.01,
"hours": 8, "aprPct": 10.95, "nextFundingMs": 1793577600000 } ] }Fees
GET /v1/fees returns each exchange's published taker fees in bps, per fill. Net figures use the base rate. To work out net at your own fee on a size S:
{ "rows": [ { "venue": "Binance", "spotTakerBps": 10, "perpTakerBps": 5 } ] }
fills = 1 if strategy == "spot" else 2 # a hedged route pays again to close
shift = fills * ((paid_buy - mine) + (paid_sell - mine) * (1 + grossBps / 10000))
net = netUsd[S] + S * shift / 10000Spread
GET /v1/spread/:id returns the net spread of a route, live while it is open and as saved after it closes. URL-encode the id. Add from or to, in unix milliseconds, to read the route across all of its saved history in that range: at most 2,000 points, oldest first, keeping the peaks.
curl -H "X-API-Key: $ARBITRAGEX_KEY" \
"https://api.arbitragex.io/v1/spread/BTC%7CBinance%3Aspot%7COKX%3Aperp"
{ "points": [ { "t": 1793570209670, "netBps": 2.1 } ], "historyFromMs": 0 }Proof Log
GET /v1/proof?limit=50 returns closed opportunities that lasted at least 30 seconds, newest first. limit is at most 500. A route that returns within 5 minutes widens its last row, so a row can move back to the top.
from,to: unix milliseconds, on when the row closed.coin: one coin.cursor: thenextvalue of the last page.nextis null on the last page.
{ "rows": [ { "id": "BTC|Binance:spot|OKX:perp", "coin": "BTC", "strategy": "spot_perp",
"buyVenue": "Binance", "sellVenue": "OKX", "peakNetBps": 4.2, "maxSizeUsd": 21400,
"openNetBps": 3.8, "openSizeUsd": 18200, "closeNetBps": 1.1, "closeSizeUsd": 9600,
"status": "verified", "openedMs": 1793570209670, "closedMs": 1793570389670 } ],
"next": "1793570389670:48121", "historyFromMs": 0 }Websocket
Connect to wss://api.arbitragex.io/v1/stream with the key in the X-API-Key header. You get a snapshot of every row, then batches at most every 250 ms. A client that falls about 4 seconds behind is closed, and gets a fresh snapshot when it reconnects.
import WebSocket from "ws";
const rows = new Map();
function connect() {
const socket = new WebSocket("wss://api.arbitragex.io/v1/stream", {
headers: { "X-API-Key": process.env.ARBITRAGEX_KEY },
});
socket.on("message", (data) => {
const message = JSON.parse(data);
if (message.type === "snapshot") {
rows.clear();
for (const row of message.rows) rows.set(row.id, row);
}
if (message.type === "upsert") for (const row of message.rows) rows.set(row.id, row);
if (message.type === "remove") for (const id of message.ids) rows.delete(id);
});
socket.on("close", () => setTimeout(connect, 1000));
}
connect();snapshot:{ type, rows, delayedMs }, always first.upsert:{ type, rows }, new or changed rows, at most 400 in a batch.remove:{ type, ids }, rows that are gone.health:{ type, venues, medianLatencyMs }, every 5 seconds.
Types
strategy:spot,spot_perporperp_perp.status:verifiedorcaution, with plain words inreasons.transfer:open,unknownornot_needed.netUsd: estimated profit at each size.nullmeans the books can't fill it.netBps: after costs at $1,000, or at the most profitable size if $1,000 loses.- Times are unix milliseconds. Prices are USDT per whole coin.
Net figures are estimates, not fills. See how net is calculated. You are responsible for anything you automate.