Cross-Exchange Arbitrage API
Cross-exchange arbitrage exploits funding rate differentials for the same asset across two venues: long where funding is lowest, short where it is highest. The board covers every funding venue (30+ CEXs and perp DEXs; a venue whose markets are halted is left out) across every asset class — crypto plus RWA perps (tokenized equities, commodities, FX, indices) — keeps one row per asset, and ranks executable pairs first by net APR after both venues' taker fees and the adverse entry spread. Pairs never cross asset classes, an RWA leg whose funding reads exactly 0 (paused for its session, or a market that never funds) is not paired, and unknown market data is null, never zero.
List cross-exchange opportunities
Returns one row per asset: its executable pair with the highest netApr, else its best no_depth pair (book-priced, a venue reports no size), else its largest gross differential (indicative). Legs pair within one instrument-master asset, so lot spellings of one coin (BONK, 1000BONK, KBONK) pair with each other and prices compare per asset unit. Rows rank executable first by netApr, then no_depth rows (a venue reports no book size), then the other indicative rows, each by netApr where they have one, else gross apr.
The board is composed every 5 minutes by a scheduled job, never per request, and is stale after 10 minutes; each pair is priced again at the time of the request from its legs' own quote times, so a quote older than 10 minutes makes the row indicative (stale_quote). An asset needs current funding with a verified per-market settlement interval on at least two selected venues. Only funding rates inside the 10-minute freshness window are paired, and a venue that reports a market as not trading (halted, settling, delisted or pre-launch) is left out for it. With no board to serve the endpoint answers 503 (service_unavailable, Retry-After: 30).
Query parameters
- Name
exchanges- Type
- string
- Description
Comma-separated list of venues to include, e.g.
Binance,OKX,Bybit,Hyperliquid,WhiteBIT,Kraken,Lighter. Omit to include all venues.X-Data-Venueslists the venues a filter can offer.
- Name
minOiUsd- Type
- number
- Description
Minimum open interest in USD on the smaller leg. Defaults to
0(no floor). A leg with unknown OI fails a positive floor. The floor applies to each asset's chosen pair: a failing asset leaves, it is not re-paired.
- Name
minVolUsd- Type
- number
- Description
Minimum 24h perp volume in USD on the smaller leg. Defaults to
0(no floor). A leg with unknown volume fails a positive floor.
- Name
assetClass- Type
- string
- Description
Underlying filter. One of
all(default),crypto, orrwa— the latter covering equity, commodity, FX and index perps. Pairing always stays within one asset class.
- Name
sector- Type
- string
- Description
Narrative filter. One of
all(default),memes,ai-agents,layer-1ordefi: keeps only crypto coins in that narrative's CoinGecko category, the constituents the narratives endpoint lists. A coin the category does not list, and every RWA perp, is left out when a sector is set. The free route reads it too.
- Name
limit- Type
- integer
- Description
Return at most this many rows, the first in board order. Omit it (and
cursor) for every row. On v1 a limited answer carriespagination.
- Name
cursor- Type
- string
- Description
Opaque cursor from the previous page's
pagination.cursor(100 rows a page whenlimitis omitted).
Response headers
Besides the freshness headers, both the v1 and the free route send:
X-Data-Missing: each configured venue that is not a quoted member of the board, comma-separated asVenue:reason.not_live(every contract halted; the venue is not asked),stale(no funding row inside the funding SLA),no_ticker(its rows are served unpriced: its ticker or book reads failed). edgeX, ApeX and GRVT publish no bulk ticker, so their legs are priced from per-contract top-of-book reads of the board's contracts (up to 60 per venue, 5 at a time, cached 60 s). Absent when every venue is a quoted member.X-Data-Venues: the venues a venue filter may offer, comma-separated: the configured venues less thosenot_liveorstale(a venue whose funding rows are all past the funding SLA, such as a halted venue, is not offered).
Request
curl -G https://www.sharpe.ai/api/v1/arbitrage/cross-exchange \
-H "Authorization: Bearer sk_live_your_key_here" \
-d exchanges=Binance,OKX,Bybit,Aster,Gate.io \
-d limit=50
Response
{
"data": [
{
"symbol": "ZCAT",
"assetClass": "crypto",
"longExchange": "Aster",
"shortExchange": "Gate.io",
"longRate": -0.0054679,
"shortRate": -0.000003,
"netFundingRate": 0.00546715,
"apr": 47.892234,
"grossApr": 47.892234,
"netApr": 47.600961,
"spreadRate": 0.02214022,
"spreadSource": "book",
"longAsk": 0.08357,
"shortBid": 0.08174,
"executableDepthUsd": null,
"holdingDays": 30,
"executionStatus": "indicative",
"oiLong": null,
"oiShort": 148993.29,
"volumeLong24hUsd": 141557.06,
"volumeShort24hUsd": 218730,
"nextSettlement": "2026-09-27T00:00:00.000Z",
"intervalHours": 1,
"pairStatus": "eligible",
"fundingAsOf": "2026-09-26T23:47:05.000Z",
"pricesAsOf": "2026-09-26T23:50:38.000Z",
"indicativeReason": "no_depth",
"roundTripFeeRate": 0.0018,
"breakEvenDays": 0.18,
"longSymbol": "ZCATUSDT",
"shortSymbol": "ZCAT_USDT",
"longIntervalHours": 1,
"shortIntervalHours": 4,
"longPricesAsOf": "2026-09-26T23:50:38.000Z",
"shortPricesAsOf": "2026-09-26T23:50:41.000Z"
}
],
"pagination": { "cursor": "NTA", "has_more": true, "total": 1017 },
"meta": {
"request_id": "req_def456ghi789jklm",
"timestamp": "2026-09-26T23:51:19.000Z",
"elapsed_ms": 12
}
}
Formulas
All rates are decimal fractions. Each leg's funding rate is per its own market's interval (longIntervalHours, shortIntervalHours); both are normalized to hourly before they are compared, so mixed 1h/4h/8h legs compare correctly. A tool that annualizes every rate as if it were 8-hourly shows a 1-hour leg 8× smaller and a 4-hour leg 2× smaller than it pays.
netFundingRate = (shortRate / shortIntervalHours − longRate / longIntervalHours) × intervalHours, at the faster leg's cadence (intervalHours).apr = |netFundingRate| × 8760 / intervalHours(gross, simple).spreadRate = (longAsk − shortBid) / midpointfrom both books (spreadSource: book), else from last or mark prices (reference), elsenull. Prices are per asset unit (a1000PEPEcontract's price ÷ 1000).roundTripFeeRate = 2 × long-venue taker fee + 2 × short-venue taker fee, each venue's standard (VIP 0) perpetual rate.netApr = apr − (roundTripFeeRate + max(spreadRate, 0)) × 365 / 30, only on book-priced rows. A favourable (negative) spread is never credited as income.breakEvenDays = (roundTripFeeRate + max(spreadRate, 0)) ÷ (apr / 365);nullwithout anetApr.
A row is executable when its spread is book-priced, the legs' prices are within 5% of each other, both quotes are at most 10 minutes old at the time of the request, and both venues report top-of-book depth of at least $10,000. Unknown depth is not evidence of a fill. Otherwise it is indicative. The board ranks executable rows first, then no_depth rows, then the other indicative rows:
indicativeReason | Meaning |
|---|---|
unpriced | A leg has no price at all. netApr is null. |
reference_only | A leg has no book: the spread comes from last or mark prices, so netApr is null. |
price_gap | The legs are 5–20% apart (netApr kept) or more than 20% apart (likely two instruments: netApr is null). |
thin_book | A venue reports top-of-book depth under $10,000. |
stale_quote | A leg's quote is older than 10 minutes at the time of the request. |
oi_below_notional | A venue reports open interest under $10,000 on its leg. Unknown open interest is no evidence either way. |
no_depth | Every other check passes, but a venue reports no top-of-book size, so the fill at $10,000 is unproven. |
identity_mismatch, not_live, contract_type, margin_type | The pair check failed on identity, trading status or contract terms. |
Response fields
Each object in the data array is the chosen long/short pair for one asset.
- Name
symbol- Type
- string
- Description
The base asset ticker (e.g.,
BTC,ETH,TSLA).
- Name
assetClass- Type
- string
- Description
Underlying category:
crypto,equity,commodity,fx, orindex. A pair's two legs always share it.
- Name
longExchange- Type
- string
- Description
The exchange where you open the long position (receiving funding or paying the least).
- Name
shortExchange- Type
- string
- Description
The exchange where you open the short position (receiving the most funding).
- Name
longSymbol- Type
- string | null
- Description
The long venue's own contract symbol (e.g.
1000PEPEUSDT,kPEPE).
- Name
shortSymbol- Type
- string | null
- Description
The short venue's own contract symbol.
- Name
longRate- Type
- number
- Description
Current funding rate on the long leg, per
longIntervalHours.
- Name
shortRate- Type
- number
- Description
Current funding rate on the short leg, per
shortIntervalHours.
- Name
longIntervalHours- Type
- number | null
- Description
The long leg's own funding interval in hours.
- Name
shortIntervalHours- Type
- number | null
- Description
The short leg's own funding interval in hours.
- Name
netFundingRate- Type
- number
- Description
The differential you collect per
intervalHoursperiod (see Formulas).
- Name
intervalHours- Type
- number
- Description
The faster of the two legs' intervals: the cadence
netFundingRateis expressed at.
- Name
apr- Type
- number
- Description
Gross annualized funding differential. Fees and the entry spread are charged in
netApr.
- Name
grossApr- Type
- number
- Description
Deprecated alias of
apr.
- Name
netApr- Type
- number | null
- Description
aprafter both venues' taker fees and the adverse entry spread over a 30-day hold. Present only on book-priced rows; every executable row has one.
- Name
roundTripFeeRate- Type
- number | null
- Description
Two perpetual taker fills on each venue at its standard fee, as a fraction of notional.
- Name
breakEvenDays- Type
- number | null
- Description
Days of carry that pay back the fees and adverse entry spread.
nullwithout anetApr.
- Name
spreadRate- Type
- number | null
- Description
Signed entry spread (see Formulas);
nullwhen either leg has no price.
- Name
spreadSource- Type
- string | null
- Description
book(both order books),reference(last or mark prices), ornull.
- Name
longAsk- Type
- number | null
- Description
Best ask on the long venue, per asset unit.
- Name
shortBid- Type
- number | null
- Description
Best bid on the short venue, per asset unit.
- Name
executableDepthUsd- Type
- number | null
- Description
The smaller of the long venue's ask size and the short venue's bid size in USD, among the venues that report one. With only one venue reporting it is that leg's figure, an upper bound, and the row is
indicative;nullwhen neither venue reports depth.
- Name
holdingDays- Type
- number
- Description
Cost-amortization horizon for
netApr. Currently30.
- Name
executionStatus- Type
- string
- Description
executableorindicative(see Formulas). Indicative rows keep every figure and rank below executable ones.
- Name
indicativeReason- Type
- string | null
- Description
Why the row is
indicative(table above);nullwhen executable.
- Name
pairStatus- Type
- string
- Description
Whether the legs are one instrument:
eligible,unpriced(a leg has no price to check), else the first check that failed:identity_mismatch,not_live,contract_type,margin_typeorprice_gap(more than 20% apart).
- Name
oiLong- Type
- number | null
- Description
Open interest in USD on the long leg, from the venue's ticker, else the contract's latest stored open interest.
nullwhen unknown, never0.
- Name
oiShort- Type
- number | null
- Description
Open interest in USD on the short leg.
nullwhen unknown, never0.
- Name
volumeLong24hUsd- Type
- number | null
- Description
24h perp turnover in USD on the long leg, or
nullwhen the venue does not report it.
- Name
volumeShort24hUsd- Type
- number | null
- Description
24h perp turnover in USD on the short leg, or
nullwhen unknown.
- Name
nextSettlement- Type
- string | null
- Description
ISO 8601 time of the sooner of the two legs' next settlements, or
nullwhen neither venue reports it.
- Name
longNextSettlement- Type
- string | null
- Description
The long leg's own next settlement. On a mixed-interval pair (4h against 8h) the slower leg's larger print lands at its own time, which
nextSettlementdoes not show.
- Name
shortNextSettlement- Type
- string | null
- Description
The short leg's own next settlement, or
nullwhen the venue does not report it.
- Name
fundingAsOf- Type
- string | null
- Description
ISO 8601 time the older leg's funding rate was written.
- Name
pricesAsOf- Type
- string | null
- Description
When the older of the two legs' quotes was read;
nullwhen neither leg has a quote.
- Name
longPricesAsOf- Type
- string | null
- Description
When the long venue's quote was read.
- Name
shortPricesAsOf- Type
- string | null
- Description
When the short venue's quote was read.
Indicative row (a leg without a price)
{
"symbol": "BTC",
"assetClass": "crypto",
"longExchange": "Aster",
"shortExchange": "Extended",
"longRate": -0.00012237,
"shortRate": 0.000013,
"netFundingRate": 0.00002829625,
"apr": 0.24787515,
"grossApr": 0.24787515,
"netApr": null,
"spreadRate": null,
"spreadSource": null,
"longAsk": 84393.1,
"shortBid": null,
"executableDepthUsd": null,
"holdingDays": 30,
"executionStatus": "indicative",
"oiLong": null,
"oiShort": 42173242.55,
"volumeLong24hUsd": 139787090.28,
"volumeShort24hUsd": null,
"nextSettlement": "2026-09-27T00:00:00.000Z",
"intervalHours": 1,
"pairStatus": "unpriced",
"fundingAsOf": "2026-09-26T23:47:05.000Z",
"pricesAsOf": "2026-09-26T23:50:38.000Z",
"indicativeReason": "unpriced",
"roundTripFeeRate": 0.0013,
"breakEvenDays": null,
"longSymbol": "BTCUSDT",
"shortSymbol": "BTC-USD",
"longIntervalHours": 8,
"shortIntervalHours": 1,
"longPricesAsOf": "2026-09-26T23:50:38.000Z",
"shortPricesAsOf": null
}