# Price a swap

`POST /v1/swaps/price`

Returns what a swap would spend and receive, without steps. Use it while the user chooses an amount. A price holds nothing and stores nothing, so you can call it again each time the amount changes. When the user is ready to sign, [create a swap](/reference/swaps).

## Request

```bash
curl https://api.circuitry.company/v1/swaps/price \
  -H "Authorization: Bearer $CIRCUITRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "base",
    "input": { "asset": { "type": "fx", "currency": "USD" }, "amount": "20" },
    "output": { "asset": { "type": "crypto", "symbol": "ETH" } }
  }'
```

| Field | Meaning |
| --- | --- |
| `chain` | `base` or `robinhood`. The whole swap runs on this chain. `any` isn't available yet, and fails with `no_route` and `param` set to `chain` |
| `input.asset` | What to spend. See [Assets](#assets) |
| `output.asset` | What to receive |
| `input.amount` or `output.amount` | Exactly one. A decimal string in the asset's unit, such as `"20"` or `"0.25"`. `input.amount` spends an exact amount, and `output.amount` receives an exact amount |
| `account` | Optional. The address that would hold the input. With it, the price uses only tokens that the address holds enough of. The price also warns when the address may not hold enough ETH for the network fee |
| `accountCanBatch` | Optional. It has no effect on a price. A price takes it so that the same request can create a swap |

### Assets

| Type | Fields | Example |
| --- | --- | --- |
| `fx` | `currency`, an ISO 4217 code | `{ "type": "fx", "currency": "EUR" }` |
| `crypto` | `symbol` | `{ "type": "crypto", "symbol": "BTC" }` |
| `token` | `address`, the token's contract | `{ "type": "token", "address": "0x…" }` |

* An `fx` or `crypto` asset can have more than one token on a chain. The quote uses the token that gives the best result after the network fee, and the response names that token. [List assets](/reference/assets) shows the tokens for each asset.
* `{ "type": "crypto", "symbol": "ETH" }` spends and receives only native ETH, never wrapped ETH. To spend or receive WETH, give its address as a `token`.
* ETH and WETH are the same asset, so a request with ETH on one side and WETH on the other fails with `same_asset`. To wrap or unwrap ETH, call the WETH contract's `deposit` or `withdraw` function.
* A `token` can be any ERC-20 token on the chain. A token that isn't in [List assets](/reference/assets) gets the `unlisted_token` warning, except the chain's WETH.
* Tokens that take a fee on transfer aren't supported. A price for one can succeed, because a price doesn't simulate the swap, but a swap for one fails with `no_route`.
* Stocks aren't available yet, and a stock ticker or stock token fails with `stocks_not_enabled`.

## Response

```json
{
  "object": "swap_price",
  "expiresAt": "2026-10-15T01:39:19.053Z",
  "chain": "base",
  "chainId": 8453,
  "input": {
    "asset": {
      "type": "fx",
      "currency": "USD",
      "token": {
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "name": "USD Coin",
        "decimals": 6
      }
    },
    "amount": { "value": "20", "raw": "20000000" },
    "maximum": { "value": "20", "raw": "20000000" }
  },
  "output": {
    "asset": {
      "type": "crypto",
      "symbol": "ETH",
      "token": { "address": null, "symbol": "ETH", "name": "Ether", "decimals": 18 }
    },
    "amount": { "value": "0.008265758119362246", "raw": "8265758119362246" },
    "minimum": { "value": "0.008224429328765434", "raw": "8224429328765434" }
  },
  "fees": [
    {
      "type": "network",
      "estimated": true,
      "amount": { "value": "0.00000148086", "raw": "1480860000000" }
    }
  ],
  "route": { "venues": ["uniswap"] },
  "warnings": []
}
```

| Field | Meaning |
| --- | --- |
| `object` | Always `swap_price` |
| `expiresAt` | When the price is stale. A price isn't a commitment, so this is a hint for when to ask again |
| `chain` and `chainId` | The chain the swap runs on |
| `account` | The `account` from the request, if it had one |
| `input.asset` and `output.asset` | The asset from the request, with the `token` that the quote uses |
| `input.amount` | The amount the swap expects to spend |
| `input.maximum` | The most the swap can spend. It equals `input.amount` when the request gives `input.amount` |
| `output.amount` | The amount the swap expects to receive |
| `output.minimum` | The least the swap can receive. It equals `output.amount` when the request gives `output.amount` |
| `fees` | The network fee, an estimate in ETH. Circuitry charges no fee |
| `route.venues` | The venues the swap uses: `uniswap` or `aerodrome` |
| `warnings` | Codes with messages for things the user may want to know. See [Warnings](#warnings) |

Each amount has `value`, a decimal string in the asset's unit, and `raw`, the integer in the token's base units.

Circuitry sets the gap between each amount and its bound from the route, and the request can't change it. The gap is 0.1% when both tokens are stablecoins and 0.5% for other pairs, plus 0.1% when the route goes through two pools.

A large swap moves the pool's price, so it gets a worse rate than a small swap on the same route. Circuitry compares each quote's rate with the rate for a small amount on the same route. When the rate is 3% worse or more, the response has the `high_price_impact` warning. When it is more than 15% worse, the request fails with `insufficient_liquidity`. Circuitry doesn't measure swaps that are too small to quote precisely.

### Warnings

| Code | Meaning |
| --- | --- |
| `unlisted_token` | A `token` asset isn't in Circuitry's list of assets. Check that the address is the token you mean |
| `insufficient_gas` | The `account` may not hold enough ETH for the network fee. A paymaster can still pay it |
| `high_price_impact` | The swap's size makes its rate at least 3% worse than a small swap's. The message gives the percentage |

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | A field is missing, malformed, or not one this endpoint takes. `param` names it |
| 400 | `amount_too_precise` | The amount has more decimal places than every token for the asset |
| 400 | `same_asset` | The input and the output are the same asset. ETH and WETH are the same asset |
| 403 | `stocks_not_enabled` | The request names a stock |
| 403 | `sanctioned_address` | The `account` is on a sanctions list |
| 422 | `unsupported_asset` | Circuitry doesn't support the asset |
| 422 | `asset_not_on_chain` | The asset has no token on the chain, or no token exists at the address |
| 422 | `no_route` | No venue on the chain trades the pair. A swap also fails with `no_route` when its best route fails in simulation, as it does for a token that takes a fee on transfer |
| 422 | `insufficient_liquidity` | Venues trade the pair, but none can fill the amount at a rate within 15% of a small swap's. `param` names the amount. A smaller amount can succeed |
| 422 | `insufficient_balance` | The `account` doesn't hold enough of any token for the input |

This endpoint can also return the errors in [Failed authentication](/authentication#failed-authentication), `rate_limited`, and `internal_error`.
