Справочник API
Подключите живые сигналы к своему стеку. API только для чтения: он отдаёт рыночные данные и никогда не может выставить ордер или затронуть аккаунт на бирже. Входит в VIP Quant. Впервые в терминале? Начните с документации.
Базовый URL:
https://api.arbitragex.io
Аутентификация
Создайте ключ в настройках. Он показывается один раз и хранится только в виде хеша, поэтому держите его в безопасности и никогда не размещайте в браузере. Утерянные ключи не восстановить: отзовите и создайте новый. До 5 ключей на пользователя. Передавайте его в заголовке X-API-Key в каждом запросе, включая upgrade до websocket.
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"]);
}Отозванный ключ перестаёт работать в течение нескольких секунд, а открытые сокеты вскоре закрываются. Ключ перестаёт работать вместе с тарифом и снова заработает, если тариф вернётся.
Лимиты запросов
У каждого ключа свой лимит — по умолчанию 120 запросов в минуту, считается в минутных окнах, плюс 3 открытых сокета. Каждый ответ сообщает ваше текущее положение:
X-RateLimit-Limit: допустимое число запросов за окно.X-RateLimit-Remaining: сколько запросов осталось в этом окне.X-RateLimit-Reset: секунд до конца окна.
Для живых строк лучше использовать websocket. Он присылает изменения сразу, а открытый сокет не расходует лимит.
Ошибки
Ошибки возвращаются в JSON: { "error": "Rate limit reached." }
- 401: ключ неверный, отозван или его тариф закончился.
- 405: API принимает только GET.
- 429: достигнут лимит запросов или сокетов. Подождите
Retry-Afterсекунд. - 503: ключи сейчас не удаётся проверить. Повторите чуть позже.
OpenAPI
Весь API описан в одном документе OpenAPI 3.1 по постоянному адресу. Импортируйте его в Postman, Insomnia или Swagger UI либо сгенерируйте из него типизированный клиент. Каждый эндпоинт в нём содержит примеры на curl, Python и JavaScript, приведённые ниже.
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
Справочник эндпоинтов
Все REST-эндпоинты, сгенерированные из документа OpenAPI. Каждый пример читает ваш ключ из переменной окружения ARBITRAGEX_KEY.
- /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, обязательно: 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, обязательно: 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, обязательно: The market's venue.marketquery, обязательно: 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, обязательно: 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);Возможности
GET /v1/opportunities возвращает все живые строки, сначала с наибольшим нетто.
{
"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
}Фандинг
GET /v1/funding возвращает текущую ставку фандинга каждого перпа.
{ "rows": [ { "coin": "BTC", "venue": "Binance", "symbol": "BTCUSDT", "ratePct": 0.01,
"hours": 8, "aprPct": 10.95, "nextFundingMs": 1793577600000 } ] }Комиссии
GET /v1/fees возвращает опубликованные комиссии тейкера каждой биржи в bps за исполнение. Нетто-значения используют базовую ставку. Чтобы посчитать нетто по своей комиссии для объёма 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 / 10000Спред
GET /v1/spread/:id возвращает нетто-спред маршрута: живой, пока он открыт, и сохранённый после закрытия. Кодируйте id для URL. Добавьте from или to в миллисекундах unix, чтобы получить всю сохранённую историю маршрута в этом диапазоне: не более 2 000 точек, сначала старые, с сохранением пиков.
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 возвращает закрытые возможности, длившиеся не менее 30 секунд, сначала новые. limit — не более 500. Маршрут, вернувшийся в течение 5 минут, расширяет свою последнюю строку, поэтому строка может снова оказаться наверху.
from,to: миллисекунды unix, по времени закрытия строки.coin: одна монета.cursor: значениеnextс предыдущей страницы. На последней страницеnextравно null.
{ "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
Подключитесь к wss://api.arbitragex.io/v1/stream с ключом в заголовке X-API-Key. Вы получите snapshot всех строк, затем пакеты не чаще раза в 250 мс. Клиент, отставший примерно на 4 секунды, отключается и при переподключении получает свежий snapshot.
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 }, всегда первым.upsert:{ type, rows }, новые или изменённые строки, не более 400 в пакете.remove:{ type, ids }, исчезнувшие строки.health:{ type, venues, medianLatencyMs }, каждые 5 секунд.
Типы
strategy:spot,spot_perpилиperp_perp.status:verifiedилиcaution, с пояснениями вreasons.transfer:open,unknownилиnot_needed.netUsd: оценка прибыли для каждого объёма.null— стаканы не могут его исполнить.netBps: после затрат на 1 000 $ или на самом прибыльном объёме, если 1 000 $ убыточен.- Время — в миллисекундах unix. Цены — в USDT за целую монету.
Нетто-значения — оценки, а не исполнения. См. как считается нетто. Вы отвечаете за всё, что автоматизируете.