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

# Sort and price protection

> Order quotes by net received or by time to delivery, and set how far a non-firm quote's delivery may move.

Two request fields change what you get back without leaving any venue out:

| Field | Value | Effect |
| - | - | - |
| `sort` | `netReceived` (default) or `eta` | The order of `quotes`. |
| `priceProtectionBps` | A whole number from 0 to 1000; 30 when omitted | How far a non-firm executable quote's delivery may fall below what it quotes, in basis points (100 is 1%). |

## Sort

| `sort` | Amount you send | Order of `quotes` |
| - | - | - |
| `netReceived` | `sellAmount` | Most `netReceived` first. |
| `netReceived` | `buyAmount` | Least `sell.amount` first. |
| `eta` | either | Shortest `eta` first. |

Under every order:

* a quote whose value cannot be read goes after those that can: an `unavailable` `netReceived`, or no `eta`;
* quotes that tie come first if every fee is known, then in order of venue id.

`eta` is an ISO 8601 duration such as `PT10S`. Quotes on your venue accounts may carry no `eta`; with `sort: "eta"` they then come after quotes that have one, in order of venue id.

```bash cURL theme={null}
# Order quotes by expected time to delivery, fastest first, instead of by net received.
request=$(jq -nc --arg sell "$sell" --arg buy "$buy" --arg amount "$amount" '{
  sell: {asset: $sell},
  buy: {asset: $buy},
  sellAmount: $amount,
  sort: "eta"
}')
by_eta=$(quote_request "$request")
jq -c '[.quotes[] | {venue: .legs[0].venue.id, eta, netReceived}]' <<<"$by_eta" >&2
```

```typescript TypeScript SDK theme={null}
// Fastest first. Venue accounts trade inside the account, so their quotes often carry no estimate.
const byEta = await client.quote({ ...base, sort: 'eta' });
const etas = byEta.quotes.map((quote) => seconds(quote.eta)).filter((value): value is number => value !== null);
```

Sorting asks the same venues and returns the same quotes, in a different order.

## Price protection

A non-firm executable quote may deliver a little less than it quotes. `priceProtectionBps` sets the most it may fall, and the quote states the result:

| Quote | Field | Value |
| - | - | - |
| `executable` with `firm: false`, Basic or Advanced | `minOut` | The least you receive: the quoted amount less the protection, rounded down to the precision of the buy amount. |
| `buyAmount`, from one of your venue accounts | `maxPay` | The most you sell. On a quote the venue has firmly priced, the amount it would take from your account, raised by the protection percentage and rounded up. Until then, the same as `sell.amount`. |
| `firm: true` or `indicative` | `minOut` | `null`. A firm quote delivers exactly its amount; an indicative quote commits to nothing. |

If you omit `priceProtectionBps`, 30 (0.3%) is used.

```bash cURL theme={null}
# Set how far a non-firm executable quote's delivery may fall below what it quotes. It sets
# minOut on sell-exact quotes, and maxPay on buy-exact quotes from your venue accounts.
request=$(jq -nc --arg sell "$sell" --arg buy "$buy" --arg amount "$protection_amount" \
  --argjson bps "$protection_bps" '{
  sell: {asset: $sell},
  buy: {asset: $buy},
  sellAmount: $amount,
  priceProtectionBps: $bps
}')
protected=$(quote_request "$request")
jq -c '[.quotes[] | select(.minOut != null) | {venue: .legs[0].venue.id, buy: .buy.amount, minOut}]' <<<"$protected" >&2
request=$(jq -nc --arg sell "$sell" --arg buy "$buy" --arg amount "$buy_amount" \
  --argjson bps "$protection_bps" '{
  sell: {asset: $sell},
  buy: {asset: $buy},
  buyAmount: $amount,
  priceProtectionBps: $bps
}')
protected_exact=$(quote_request "$request")
jq -c '[.quotes[] | {venue: .legs[0].venue.id, firm, sell: .sell.amount, maxPay}]' <<<"$protected_exact" >&2
```

```typescript TypeScript SDK theme={null}
// Accept at most 0.5% (50 basis points) less than quoted. Compare `minOut` with `receive.amount`.
const protectedSet = await client.quote({ ...base, priceProtectionBps: 50 });
const protection = protectedSet.quotes.map((quote) => ({ quoteId: quote.id, venue: quote.legs.map((leg) => leg.venue.id).join(' + '),
  firm: quote.firm, expiresAt: quote.expiresAt, receive: quote.buy.amount, minOut: quote.minOut }));
```

For example, 100 basis points on a quote to buy `0.4322` ETH gives a `minOut` of `0.4278`:

```json Output theme={null}
{ "venue": "...", "buy": "0.4322", "minOut": "0.4278" }
```

Rounding is to the buy amount's own precision, so on a small amount quoted with few decimals, two protection values can give the same `minOut`.

### Protection on a requote and an order

* A [requote](https://tetrafi.mintlify.site/trade/requotes-and-expiry.md) can send its own `priceProtectionBps`, 0 to 1000, for the new quote's `minOut`. Without it, the new quote keeps the original's protection.
* A Basic quote takes no `priceProtectionBps` when you order it: sending one is refused with `400 INVALID_REQUEST`. Set your floor with `minReceive` instead, which a venue that executes within your limit requires on a quote that is not firm.

## Refusals

| Code | Why | What to do |
| - | - | - |
| `INVALID_REQUEST` | `priceProtectionBps` is above 1000, negative or not a whole number, or `sort` is not `netReceived` or `eta`. `message` names the field. | Send a whole number from 0 to 1000, and a listed `sort`. |

## In the app

On the Trade page, open **Advanced filters**:

* **Protection** sets the price protection: 0.1%, 0.3%, 0.5% or 1%, or **Custom** up to 10%. The app starts at 0.5% (50 basis points), not the API's 0.3% default.
* **Rank by** sets the sort: **Best net** (`netReceived`) or **Fastest** (`eta`). The results list's sort select sets the same order; when you fix the amount you buy, it reads **Least paid first**.


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