> ## 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.

# Limit orders

> How limit orders work: what an order sells, how priceTrigger is expressed, the order lifecycle, what can't be sold, and how the execution gas is paid.

A limit order sells a set amount of one token for another when the market reaches your price. You create it with one call to [`POST /limit-order`](/api-reference/limit-orders/create-limit-order). Olympex then watches the price and executes the swap from the maker's wallet, under the maker's pair signature and token allowance. Nothing moves when you create the order, and you follow it by polling its status.

## The model

An order sells `amount` of `inTokenAddress` for `outTokenAddress`, on one chain, for one maker:

| Part | Fields | What to send |
| - | - | - |
| The maker | `accountTo` | The wallet that sells, receives the output and signs the pair. It must be an EOA. Send the EIP-55 checksummed form. |
| The pair | `chainId`, `inTokenAddress`, `outTokenAddress` | The token you sell and the token you buy, on a chain from [`GET /chains`](/api-reference/chains/list-chains) that has an [order contract](/concepts/order-authorization#the-order-contract). |
| The symbols | `tokenASymbol`, `tokenBSymbol` | The real symbols of the token you sell and the token you buy. Olympex can use them to find a reference price and doesn't check them: see [Reference price](#reference-price). |
| The terms | `amount`, `priceTrigger`, `price`, `expired`, `slippage`, `gasPrice` | See the units below. |
| The authorization | `signature` | The maker's signature for the pair. See [Order signatures and allowances](/concepts/order-authorization). |

`accountTo`, `inTokenAddress` and `outTokenAddress` must be valid EVM addresses: `0x` and 40 hexadecimal digits, in lowercase or EIP-55 checksummed form. A mixed-case address with a wrong checksum returns `400 VALIDATION_ERROR` with `"Must be a valid EVM address"`. Send only the fields in the table above. Fields that Olympex sets as it executes an order, such as `status`, `txHash` and `reasonFail`, return `400 VALIDATION_ERROR`, and every new order starts `pending`.

| Field | Unit | Example |
| - | - | - |
| `amount` | Human-readable amount of `inTokenAddress`, as a decimal string. Not base units. | `"0.5"` is 0.5 WETH. |
| `priceTrigger` | Units of `outTokenAddress` per 1 `inTokenAddress`, human-readable, as a decimal string. | `"4200"` is 4,200 USDC per WETH. |
| `price` | Optional. A copy of the limit price that Olympex stores as sent and never syncs with `priceTrigger`. Send the same value as `priceTrigger`, and send both whenever you change one. Responses return `0` when you omit it. | `"4200"` |
| `expired` | A Unix timestamp in milliseconds, as a 13-digit string, in the future and at most 365 days ahead. Any other value returns `400 VALIDATION_ERROR`. See [Expiry](#expiry). | `String(Date.now() + 7 * 86_400_000)`: one week from now |
| `slippage` | Maximum slippage at execution, in percent, as a decimal string. | `"1"` is 1%. |
| `gasPrice` | The chain's current gas price in gwei, rounded up, as a string. Olympex stores it with the order. It isn't a cap: execution pays the gas price at that moment. See [Funds and fees](#funds-and-fees). | `"35"` |
| `chainId` | An integer, in requests and responses. A string returns `400 VALIDATION_ERROR`. | `137` |

Responses return `amount`, `price` and `priceTrigger` as JSON numbers. Olympex stores them as double-precision numbers, which keep 15 to 17 significant digits: an `amount` of `"0.123456789123456789"` is stored and returned as `0.12345678912345678`. Send at most 15 significant digits, so the stored value equals the one you sent.

## Price direction

`priceTrigger` is the price of the token you sell, expressed in the token you buy. The order executes when the market price of `inTokenAddress`, in `outTokenAddress`, reaches `priceTrigger` or better.

For an order that sells `"0.5"` WETH for USDC with a `priceTrigger` of `"4200"`, the trigger is 4,200 USDC per WETH. At that price, 0.5 WETH is worth 2,100 USDC.

<Warning>
  Check the orientation before you send an order. A trigger written as WETH per USDC (`1/4200`, about `0.000238`) is read as USDC per WETH, which is a different price.
</Warning>

## Reference price

Olympex needs a reference price for the pair. When you create an order, it reuses a recent lookup for the same chain and token addresses, written exactly the same way, or looks up a market for the two symbols, or identifies the tokens by address. When all of these fail, the call returns `400` and no order is created. In this example, the two addresses aren't token contracts:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Not exist reference price for this pair FOOZZ/BARZZ",
    "details": [
      {
        "message": "Not exist pair for create limit order, you must be select a new pair (Not possible create limit order, you must be select a new pair | > Error => Could not recover data of contract)"
      }
    ]
  },
  "meta": {
    "requestId": "EdkfSjF0IAMEPEg=",
    "version": "v1",
    "accountType": "integrator",
    "apiKeyId": "00000000-0000-4000-8000-000000000000"
  }
}
```

* Send each token's real symbol in `tokenASymbol` (the token you sell) and `tokenBSymbol` (the token you buy). Olympex doesn't check them against the token contracts. Read `symbol()` from each token contract, or take the symbol from [`GET /tokens`](/api-reference/tokens/list-tokens) by address, trimmed and without a trailing `_<number>`: the list tells duplicate symbols apart with suffixes such as `STRK_1`, so send `STRK`.
* Responses can return a normalized symbol: `WETH` becomes `ETH`, `WBNB` becomes `BNB` and `WPOL` becomes `POL`. The addresses are returned as you sent them.
* Check the symbols in the response. Olympex accepts a real but wrong symbol, such as `WBTC` on a WETH order, and then prices the order from that token's market. Cancel an order whose symbols match neither your tokens' symbols nor their normalized forms.
* The same error can occur when Olympex can't reach its price data at that moment. If you're sure of the pair and the symbols, retry later with backoff before you rule the pair out.

## Lifecycle

| Status | Meaning | What you can do |
| - | - | - |
| `pending` | Waiting for the price. New orders start here. | Update it with `PATCH`, or cancel it with `DELETE`. |
| `executing`, `submitted` | Olympex is executing the order. | Keep polling. |
| `completed` | Executed. `txHash` is the execution transaction on `chainId`. | Nothing: the order is final. |
| `failed` | Execution failed. `reasonFail[]` says why, and `attemptNumber` counts the attempts. | Nothing: the order is final. Create a new order if you still want the trade. |
| `cancelled` | Cancelled with `DELETE`. `deletedAt` is the time of that call. | Nothing: the order is final, and stays readable. |

Treat any status not in this table as not final. `deletedAt` is an empty string until the order is cancelled.

Poll [`GET /limit-order/{id}`](/api-reference/limit-orders/get-limit-order) to follow an order's status.

### Expiry

`expired` must be a Unix timestamp in milliseconds, as a 13-digit string, in the future and at most 365 days ahead. `POST` requires it and `PATCH` can change it. Any other value, such as a time in seconds, an ISO 8601 date or a past time, returns `400 VALIDATION_ERROR`. No documented `status` marks an expired order. Don't rely on `expired` to stop an order: when you no longer want it, cancel it with `DELETE` and lower the allowance.

### Update a pending order

[`PATCH /limit-order/{id}`](/api-reference/limit-orders/update-limit-order) changes an order while it is `pending`. Send only the fields you change: `priceTrigger` and `price`, `amount`, `expired`, `slippage` or `gasPrice`. A `PATCH` with only `priceTrigger` leaves `price` unchanged, so send both. If you change `amount`, set the allowance to match. To change the tokens, the chain or the maker, cancel the order and create a new one. On an order that isn't `pending`, the call fails with `409 CONFLICT` and changes nothing.

### Cancel a pending order

[`DELETE /limit-order/{id}`](/api-reference/limit-orders/cancel-limit-order) cancels an order while it is `pending`: `status` becomes `cancelled` and `deletedAt` is set. The order stays readable and stays in the list. On an order that isn't `pending`, including one that is already `cancelled` or `completed`, the call fails with `409 CONFLICT` and changes nothing. After a timeout, read the order before you send the call again.

Cancelling doesn't touch the maker's token allowance. Lower it to what the remaining orders need, or set it to `0` to stop every order that sells the token: see [Stop everything](/concepts/order-authorization#stop-everything-set-the-allowance-to-0).

### List your orders

[`GET /limit-order`](/api-reference/limit-orders/list-limit-orders) takes the optional filters `status`, `chainId` and `accountTo`, combined with AND.

* It ignores any other query parameter, so a misspelt filter returns every order. Send each parameter once: a repeated parameter matches nothing.
* The list returns only orders created with your API key, including cancelled ones. An order ID from another API key returns `404 NOT_FOUND`.
* It is unsorted and unpaginated: sort by `createdAt` yourself.
* `accountTo` is stored exactly as you sent it, and `?accountTo=` matches case-sensitively. Send the EIP-55 checksummed form everywhere.

## Creating an order is not safe to repeat

Each successful `POST /limit-order` creates a new order, and Olympex ignores any `id` you send. If a create call times out, the order may still exist. Don't retry it blindly: list your orders for the maker and chain, and look for one that matches your pair, `amount`, `priceTrigger` and creation time before you send the call again. [Handle errors and retries](/guides/handle-errors-and-retries) covers the pattern.

## Funds and fees

* **Nothing moves at creation.** The tokens stay in the maker's wallet, and nothing is reserved. The wallet must still hold `amount` and the allowance when the order executes, or it can't execute.
* **At execution**, Olympex pulls `amount` of `inTokenAddress` from `accountTo` through the Olympex order contract, swaps it, and sends all the output to `accountTo`.
* **Gas is reimbursed in the token sold.** Olympex pays the execution gas and is reimbursed from `accountTo` in `inTokenAddress`, as a second transfer. The maker needs no native token for the execution, only for its own approvals.
* **The reimbursement is the real gas cost.** It is the execution transaction's gas used times its actual gas price, converted to `inTokenAddress`. The order's `gasPrice` isn't a cap: neither the API nor the order contract enforces it.
* **So the allowance must cover `amount` plus the gas cost in `inTokenAddress`.** Estimate that cost with a single-chain [`POST /quotes`](/api-reference/quotes/get-quote) and `"includeGasInfo": true`: `dataFeeTransaction.transactionFeeInToken` is the estimate, and `valueToApprove` is `amount` plus it. Size the allowance from that estimate plus a generous buffer, not from `gasPrice`: the estimate leaves out the Olympex contracts, and gas prices move before the order executes. See [Gas and fees](/concepts/gas-and-fees#limit-orders-and-dca).

[Gas and fees](/concepts/gas-and-fees#limit-orders-and-dca) compares the costs of swaps, limit orders and DCA.

## What can't be sold

| Token or wallet | Why | What to do |
| - | - | - |
| Native tokens (`0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`, in any casing) | `inTokenAddress` must be an ERC-20. | Wrap first, and sell the wrapped token: WETH, WBNB or WPOL. [Token addresses](/concepts/supported-chains#token-addresses) lists their addresses. |
| ERC-20 tokens whose `transfer` and `approve` don't return a boolean | The order contract can't move them. USDT on Ethereum isn't supported as the token you sell in limit orders or DCA on Ethereum. | Offer a different token on that chain. |
| Fee-on-transfer tokens | The amount that arrives differs from the amount sent. | Don't offer them as the token you sell. |
| Smart-contract wallets as the maker | Only 65-byte ECDSA signatures are accepted. | Use an EOA as `accountTo`. |

Check these before you create an order. The create call can accept an order for one of them, and that order can't execute.

## What this means for your integration

* Express `priceTrigger` in units of the token you buy per 1 token you sell, and send `amount` as a human-readable string.
* Sign the pair once, and keep the allowance at `amount` plus the gas estimate and a buffer, summed over every open order that sells the token.
* Send the tokens' real symbols and check the ones the create call returns. Poll `GET /limit-order/{id}`, and treat unknown statuses as not final.
* Never retry a create call blindly: list your orders and match first.

## Related

<CardGroup cols={2}>
  <Card title="Order signatures and allowances" icon="signature" href="/concepts/order-authorization">
    The pair signature, the allowance and the order contract.
  </Card>

  <Card title="Place a limit order" icon="bolt" href="/guides/create-a-limit-order">
    The step-by-step guide.
  </Card>

  <Card title="Create a limit order reference" icon="code" href="/api-reference/limit-orders/create-limit-order">
    Every field of `POST /limit-order`.
  </Card>

  <Card title="DCA strategies" icon="calendar-days" href="/concepts/dca">
    Spread a purchase over time instead.
  </Card>
</CardGroup>


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