> ## Documentation Index
> Fetch the complete documentation index at: https://perps-api.vooi.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Arbitrage scanner

> Rank cross-exchange funding and price spreads, and chart a pair's history

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.

<Info>
  **Prerequisites:** none. The scanner and both candle endpoints are public — no VOOI API token is required.
</Info>

## How results are shaped

[`GET /arbitrage-scanner`](/docs/api-reference/arbitrage-scanner/list-arbitrage-scanner-opportunities) 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`](/docs/trading-markets#aliases). 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.

<Note>
  `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.
</Note>

## 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`](/docs/trading-markets#asset-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`.

<Note>
  `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.
</Note>

## 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](/docs/trading-markets#the-asset-query-parameter), and prefer `baseSymbol + quoteSymbol` when you already have the row.

* [`GET /arbitrage-scanner/funding-candles`](/docs/api-reference/arbitrage-scanner/per-minute-funding-rates-for-a-market-pair) — 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`](/docs/api-reference/arbitrage-scanner/price-spread-candles-for-a-market-pair) — 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

<CardGroup cols={2}>
  <Card title="Quotes and slippage" icon="calculator" href="/docs/trading-quotes">
    Price one leg precisely before committing
  </Card>

  <Card title="Markets" icon="list" href="/docs/trading-markets">
    Asset categories and the alias / `unifiedSymbol` key
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.