Skip to main content
The arbitrage scanner ranks cross-exchange opportunities: for each asset, every pair of venues that list it, in both trade directions, with the funding and price spread between them.
Prerequisites: none. The scanner and both candle endpoints are public — no VOOI API token is required.

How results are shaped

GET /arbitrage-scanner returns { items, total }, where items is a page of coins and total counts every coin matching your filters. Each coin nests down two levels:
  • Coin — one asset, keyed by asset: the cross-venue alias key (alias:gold) when the asset is aliased, otherwise the base symbol shared across venues. This is the same key as unifiedSymbol on GET /exchange/markets. It also carries category and exchangeCount.
  • Pair — one cross-exchange pairing of that coin, carrying the spreads.
  • Leg — the long and short side of that pair, each a market on one venue with its own price, funding rate, open interest, 24h volume, and depth estimates.
Every pair appears in both directions. Going long on venue A against short on venue B is a different row from its inverse, and you pick the one whose spread is favorable.

Reading the signs

fundingSpread1h, priceSpread, and priceSpreadAtSize are signed and direction-specific, so a pair’s two directions carry roughly opposite values. The max… fields are not symmetric that way: each is a maximum taken over its own direction alone, so one direction’s value is not the negation of the other’s. Each leg keeps its own signed funding rate.
priceSpread and priceSpreadAtSize can disagree by more than depth alone. priceSpreadAtSize is recomputed from both legs’ fill prices at the notional you asked for, while priceSpread comes from a periodic snapshot. Treat priceSpreadAtSize as the one that reflects what you would actually get, and expect null when either leg has no estimate at that notional.

Filtering and ranking

The scanner takes notionalUsd (default 1000) — the size used to estimate each leg’s slippage and depth impact from the live order book — plus the usual exchanges filter. Narrow the result set with categories (repeat the parameter for several; GET /exchange/markets/categories lists the values currently in use), symbol for one exact asset, query for a case-insensitive substring search over the asset key and leg symbols, excludeSymbols to drop base symbols, and the minFundingSpread, minPriceSpread, minOpenInterest, and minVolume thresholds. A coin survives a threshold when at least one of its pairs clears it. Rank with orderBy (default fundingSpread1h) and orderDirection (default desc); each coin ranks by its best pair. Page with limit (1–100, default 15) and offset.
openInterest ranks by the thinner leg of the pair and volume24h by both legs combined, while the spread fields rank by the pair value itself. Aliases are not accepted in excludeSymbols — pass base symbols there.

Charting one pair

Both candle endpoints take the same four parameters identifying the two legs: assetA / exchangeA and assetB / exchangeB. The asset parameters accept the same forms as asset elsewhere — see Markets, and prefer baseSymbol + quoteSymbol when you already have the row.
  • GET /arbitrage-scanner/funding-candles — per-minute funding rates for both legs over a window of limit minutes ending at dateTo (defaults to now, never later than the current minute) shifted offset minutes back. Page further into the past by increasing offset by limit. Minutes with no recorded rate are omitted rather than zero-filled, so each leg carries between zero and limit rates.
  • GET /arbitrage-scanner/price-spread-candles — stored price-spread candles at a resolution of 1m, 5m, 15m, 30m, 1h, 4h, 12h, or 1d, oldest first. Leg A is the buy (ask) side and leg B the sell (bid) side. Intervals without ticks are omitted, not zero-filled.

Next steps

Quotes and slippage

Price one leg precisely before committing

Markets

Asset categories and the alias / unifiedSymbol key