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

# Aggregation and routing

> Which liquidity sources Olympex queries, how it picks the winning route, and how to read and fall back from the route it returns.

Every single-chain quote is a comparison. Olympex asks each of its liquidity sources to price your pair and amount, ranks the quotes that come back, and returns the winner together with the order of the sources that quoted. You build the transaction with the winning source, and keep the others as fallbacks.

## Liquidity sources

Each source has an `aggregatorId`. It appears in quotes, you pass it to `POST /swap`, and you can exclude it from a quote.

| `aggregatorId` | Source | Notes |
| - | - | - |
| `okx` | OKX DEX aggregator | |
| `oneInch` | 1inch | |
| `openOceanV3` | OpenOcean, API v3 | |
| `openOceanV4` | OpenOcean, API v4 | |
| `zeroExV2AllowanceHolder` | 0x API v2 (AllowanceHolder) | |
| `uniswapV3Hermes` | Uniswap v3 pools | Doesn't support native-token input. |
| `uniswapV4Hermes` | Uniswap v4 pools | |

Use the IDs exactly as the API returns them: `oneInch`, for example, not `1inch`. Which sources quote varies by chain and over time, so read `aggregatorOrder` in each quote instead of assuming a source is available. Cross-chain transfers use a separate set of providers, covered in [Cross-chain mechanics](/concepts/cross-chain-mechanics).

## How the winner is chosen

For a single-chain quote, Olympex requests a quote from each source it doesn't exclude. Sources that fail or return nothing drop out. The rest are ranked by `params.orderBy`:

| `orderBy` | Ranks sources by |
| - | - |
| `MAX_OUT_AMOUNT` (default) | Highest `outAmount`: the most output tokens for your input. |
| `MIN_ESTIMATE_GAS` | Lowest `estimatedGas`. |
| `MAX_ESTIMATE_GAS` | Highest `estimatedGas`. |

The first source in the ranking becomes `aggregatorId`, and the full ranking is returned as `aggregatorOrder`.

`MAX_OUT_AMOUNT` compares output amounts only; it doesn't subtract gas. When gas is a large share of the trade, request `includeGasInfo: true` and weigh `dataFeeTransaction.transactionFeeInUSD` against the output before you show the quote. The gas-based orderings rely on each source's own `estimatedGas`, and a source that gives no estimate reports `"0"` or omits `estimatedGas`, so treat those rankings with the same caution as the estimates. [Gas and fees](/concepts/gas-and-fees) explains the gas fields.

`orderBy`, `excludeMetaAggregatorId`, `includeGasInfo` and `gasMultiplier` apply to single-chain quotes only.

## Excluding sources

`params.excludeMetaAggregatorId` takes an array of IDs to leave out of the comparison:

```json theme={null}
{
  "mode": "single-chain",
  "params": {
    "amount": "10",
    "chainId": 137,
    "excludeMetaAggregatorId": ["zeroExV2AllowanceHolder"],
    "gasPrice": "35",
    "inTokenAddress": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
    "outTokenAddress": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "slippage": "1"
  }
}
```

Unknown IDs are ignored, so a misspelled ID excludes nothing and returns no error. Copy IDs from the table above. Common reasons to exclude a source are a `/swap` failure you want to route around on the next quote, and your own policy about which venues you use.

## Reading the route

A single-chain quote describes the route the winning source found. This excerpt comes from a quote for 10 USDT to USDC on Polygon, with the fields that describe the route:

```json Quote excerpt theme={null}
{
  "aggregatorId": "oneInch",
  "aggregatorOrder": [
    "oneInch",
    "uniswapV3Hermes"
  ],
  "market": [],
  "routes": [
    {
      "percentage": 90.23750000000001,
      "subRoutes": [
        {
          "from": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
          "to": "0xa3fa99a148fa48d14ed51d610c367c61876997f1",
          "dexes": [
            {
              "name": "POLYGON_BALANCER_V2",
              "percentage": 100
            }
          ]
        },
        {
          "from": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
          "to": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619",
          "dexes": [
            {
              "name": "POLYGON_UNISWAP_V4",
              "percentage": 100
            }
          ]
        }
      ]
    },
    {
      "percentage": 9.762500000000001,
      "subRoutes": [
        {
          "from": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
          "to": "0xa3fa99a148fa48d14ed51d610c367c61876997f1",
          "dexes": [
            {
              "name": "POLYGON_BALANCER_V2",
              "percentage": 100
            }
          ]
        },
        {
          "from": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
          "to": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619",
          "dexes": [
            {
              "name": "POLYGON_UNISWAP_V4",
              "percentage": 100
            }
          ]
        }
      ]
    },
    {
      "percentage": 100,
      "subRoutes": [
        {
          "from": "0xa3fa99a148fa48d14ed51d610c367c61876997f1",
          "to": "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6",
          "dexes": [
            {
              "name": "POLYGON_QUICKSWAP_V3",
              "percentage": 100
            }
          ]
        }
      ]
    },
    {
      "percentage": 100,
      "subRoutes": [
        {
          "from": "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6",
          "to": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
          "dexes": [
            {
              "name": "POLYGON_DODO_V2",
              "percentage": 100
            }
          ]
        }
      ]
    },
    {
      "percentage": 100,
      "subRoutes": [
        {
          "from": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619",
          "to": "0xac0f66379a6d7801d7726d5a943356a172549adb",
          "dexes": [
            {
              "name": "POLYGON_QUICKSWAP",
              "percentage": 100
            }
          ]
        }
      ]
    },
    {
      "percentage": 100,
      "subRoutes": [
        {
          "from": "0xac0f66379a6d7801d7726d5a943356a172549adb",
          "to": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
          "dexes": [
            {
              "name": "POLYGON_UNISWAP_V3",
              "percentage": 100
            }
          ]
        }
      ]
    }
  ]
}
```

| Field | Meaning |
| - | - |
| `routes[].percentage` | The percentage the source reports for the route. Values don't always sum to 100, because later hops can appear as separate routes at 100. Display only. |
| `routes[].subRoutes[]` | The hops of the route. `from` and `to` are token addresses as the source reports them: case and the native-token address vary by source. |
| `subRoutes[].dexes[]` | The venues used for a hop. `name` identifies the venue, for example `POLYGON_UNISWAP_V3`, and `percentage` is the share of the hop routed through it. |
| `market[]` | Prices that other venues reported for the same trade, limited to those below the winning `outAmount`: `dexName`, `swapAmount` (output in base units of the output token) and `dexImageURL`. Often empty. |

The breakdown comes from the winning source in that source's own shape, and the level of detail varies by source. In the excerpt, the first two routes report 90.2375 and 9.7625 (with the source's floating-point noise), then four more routes report 100 for the later hops. Use `routes` and `market` to show users how their trade is filled, not for accounting. The amounts that count are `outAmount` from the quote and `minOutAmount` from `/swap`.

## Falling back with `aggregatorOrder`

`aggregatorOrder` lists the sources that returned a quote, best first, and starts with the winner. In the example above, `oneInch` won, and `uniswapV3Hermes` also quoted.

Fall back in either of these cases:

* [`POST /swap`](/api-reference/swap/build-swap) fails with `422 NO_ROUTE` or `500 SWAP_ERROR` for the winning source.
* `POST /swap` succeeds, but `eth_estimateGas` of its calldata reverts. A `200` doesn't guarantee that the calldata executes: a source can build calldata that reverts. When the allowance, the balance and the expiry are in order, the route itself can't execute, and building again with the same source returns the same route.

You then have two options:

* Retry `/swap` with the next ID in `aggregatorOrder`.
* Request a fresh quote, optionally excluding the failed source, and build with the new `aggregatorId`.

A cross-chain quote has no `aggregatorOrder`: request a new quote instead. Each source prices the trade separately. Read `swap.outAmount` and `swap.minOutAmount` from the fallback response and show them to the user before anyone signs: they are what the calldata commits to.

## When no route is found

If no source can quote the pair and amount, `POST /quotes` returns `422` with `NO_ROUTE`, for single-chain and cross-chain quotes:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "NO_ROUTE",
    "message": "No route found for this pair and amount. Providers may also be temporarily unavailable; retrying later can succeed.",
    "details": [
      {
        "message": "The quote could be retrieved but the route is not available"
      }
    ]
  },
  "meta": {
    "requestId": "E7ef-j_MoAMEPBw=",
    "version": "v1",
    "accountType": "integrator",
    "apiKeyId": "00000000-0000-4000-8000-000000000000"
  }
}
```

`NO_ROUTE` also covers every source failing or not answering in time, so it can be temporary: Olympex doesn't tell the two cases apart. The detail text varies by mode and cause. Branch on `error.code`, and don't parse `message` or `details`.

Retry with backoff, and sign each attempt again. If it persists, try a different amount or pair, and check that your exclusions haven't removed every source that can fill the trade.

## Quotes are not reserved

A quote reflects the sources' prices at the moment you asked. Olympex doesn't hold that price for you, so request `/swap` right after the quote and send the transaction right after `/swap`. Some routes include a market maker's firm quote (an RFQ leg) whose price expires within seconds of the build. Send those at once, and if the transaction reverts because that quote expired, build the swap again. [Slippage and price impact](/concepts/slippage-and-price-impact) covers how your slippage setting protects the trade in between.

## What this means for your integration

* Pass the quote's `aggregatorId` to `/swap` unchanged, and keep `aggregatorOrder` for fallbacks when `/swap` fails or its calldata doesn't pass `eth_estimateGas`.
* Don't reuse old quotes. Request a new one when the user changes the pair or amount, and before you build a transaction.
* Show the user `swap.outAmount` and `swap.minOutAmount`, not the quote, as the final numbers.
* Treat `routes` and `market` as display data whose shape varies by source.

## Related

<CardGroup cols={2}>
  <Card title="Slippage and price impact" icon="gauge" href="/concepts/slippage-and-price-impact">
    How much the price can move, and what your trade size costs.
  </Card>

  <Card title="Gas and fees" icon="magnifying-glass-dollar" href="/concepts/gas-and-fees">
    Gas estimates and integrator fees.
  </Card>

  <Card title="Get a quote" icon="code" href="/api-reference/quotes/get-quote">
    The `POST /quotes` reference.
  </Card>

  <Card title="Get a swap quote" icon="bolt" href="/guides/get-a-swap-quote">
    The step-by-step guide.
  </Card>
</CardGroup>


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