Skip to main content
This guide walks you through placing an order on CoW Protocol using nothing but cURL and the REST API. No SDK, no framework — just raw HTTP requests against the Sepolia testnet. By the end you will have:
  1. Fetched a quote
  2. Signed an order
  3. Submitted it to the order book
  4. Monitored it until settlement

Prerequisites

Before you start, make sure you have:
  • cURL installed (ships with macOS and most Linux distributions)
  • An Ethereum wallet with some WETH on the Sepolia testnet
    • Sepolia WETH: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14
    • Sepolia COW (buy token in this guide): 0xbe72E441BF55620febc26715db68d3494213D8Cb
  • Token approval for the GPv2VaultRelayer contract so it can spend your sell token
You must approve the GPv2VaultRelayer (0xC92E8bdf79f0507f65a392b0ab4667716BFE0110) to spend your sell token before submitting an order. Without this approval the order will revert on settlement.You can do this via Etherscan’s “Write Contract” tab, or with Foundry:
cast send 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 \
  "approve(address,uint256)" \
  0xC92E8bdf79f0507f65a392b0ab4667716BFE0110 \
  115792089237316195423570985008687907853269984665640564039457584007913129639935 \
  --rpc-url https://rpc.sepolia.org \
  --private-key $PRIVATE_KEY
All examples in this guide target the Sepolia testnet (https://api.cow.fi/sepolia/api/v1). Replace sepolia with mainnet, xdai, arbitrum_one, or base for other networks.

Walkthrough

1
Get a Quote
2
Send your trading intention to the /quote endpoint. This example sells 1 WETH for COW tokens:
3
curl -X POST "https://api.cow.fi/sepolia/api/v1/quote" \
  -H "Content-Type: application/json" \
  -d '{
    "sellToken": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
    "buyToken": "0xbe72E441BF55620febc26715db68d3494213D8Cb",
    "sellAmountBeforeFee": "1000000000000000000",
    "kind": "sell",
    "from": "0xYOUR_ADDRESS",
    "receiver": "0xYOUR_ADDRESS",
    "validFor": 1800,
    "signingScheme": "eip712"
  }'
4
Replace 0xYOUR_ADDRESS with your wallet address on Sepolia.
5
FieldDescriptionsellTokenAddress of the token you are selling (WETH)buyTokenAddress of the token you are buying (COW)sellAmountBeforeFeeAmount to sell in the token’s smallest unit (1 WETH = 10^18)kind"sell" fixes the sell amount; "buy" fixes the buy amountfromYour wallet addressreceiverAddress that receives the bought tokens (usually same as from)validForHow long the order stays valid, in secondssigningSchemeThe signing scheme you will use (eip712 recommended for EOAs)
6
Understand the Response
7
The API returns a JSON object containing the quote details. Here is a sample response (trimmed for clarity):
8
{
  "quote": {
    "sellToken": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
    "buyToken": "0xbe72e441bf55620febc26715db68d3494213d8cb",
    "sellAmount": "999375166535692640",
    "buyAmount": "191179999",
    "feeAmount": "624833464307360",
    "kind": "sell",
    "validTo": 1741700000,
    "receiver": "0xYOUR_ADDRESS",
    "appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "partiallyFillable": false
  },
  "from": "0xYOUR_ADDRESS",
  "expiration": "2026-03-11T12:30:00.000Z",
  "id": 12345,
  "verified": true
}
9
Key fields inside quote:
10
FieldDescriptionsellAmountSell amount after network costs have been deducted from your original inputbuyAmountEstimated amount you will receive, after network costs and protocol feefeeAmountNetwork costs in sell token units (gas estimation)validToUnix timestamp after which the order expires
11
For a deep dive into how these amounts relate to each other through the fee pipeline (network costs, protocol fee, partner fee, slippage), see How Intents Are Formed.
12
Sign the Order
13
CoW Protocol orders are off-chain intents that must be cryptographically signed. cURL cannot produce EIP-712 signatures on its own, so you need a signing tool.
14
Construct the signing payload
15
Using the quote response, build the order struct that must be signed. For a sell order the amounts to sign are:
16
  • sellAmount: quote.sellAmount + quote.feeAmount (the spot price / beforeAllFees amount)
  • buyAmount: quote.buyAmount with your slippage tolerance applied (e.g. subtract 0.5%)
  • feeAmount: "0" (fees are handled internally by the protocol at settlement)
  • 17
    All other fields come directly from the quote response.
    18
    {
      "sellToken": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
      "buyToken": "0xbe72E441BF55620febc26715db68d3494213D8Cb",
      "receiver": "0xYOUR_ADDRESS",
      "sellAmount": "1000000000000000000",
      "buyAmount": "190224099",
      "validTo": 1741700000,
      "feeAmount": "0",
      "kind": "sell",
      "partiallyFillable": false,
      "sellTokenBalance": "erc20",
      "buyTokenBalance": "erc20",
      "appData": "0x0000000000000000000000000000000000000000000000000000000000000000"
    }
    
    19
    The sellAmount you sign is quote.sellAmount + quote.feeAmount (the full amount before network cost deduction). The settlement contract deducts network costs itself. If you sign only quote.sellAmount, your order will sell less than intended.
    20
    EIP-712 domain
    21
    The EIP-712 domain for CoW Protocol on Sepolia is:
    22
    {
      "name": "Gnosis Protocol",
      "version": "v2",
      "chainId": 11155111,
      "verifyingContract": "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
    }
    
    23
    For details on the domain separator and order struct hashing, see Signing Schemes.
    24
    Option A: Sign with Foundry (cast)
    25
    If you have Foundry installed, you can use cast to produce the EIP-712 signature. First, compute the order digest using the helper contract on Sepolia, then sign it:
    26
    # Compute the order digest using the on-chain helper
    # (see https://sepolia.etherscan.io/address/0x59Ffd6c1823F212D49887230f155A35451FdDbfa)
    
    # Then sign the digest
    cast wallet sign \
      --private-key $PRIVATE_KEY \
      $ORDER_DIGEST
    
    27
    The output is a 65-byte hex signature you will pass as the signature field.
    28
    Option B: Sign with a Python one-liner
    29
    from eth_account import Account
    from eth_account.messages import encode_structured_data
    
    order_typed_data = {
        "types": {
            "EIP712Domain": [
                {"name": "name", "type": "string"},
                {"name": "version", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "verifyingContract", "type": "address"},
            ],
            "Order": [
                {"name": "sellToken", "type": "address"},
                {"name": "buyToken", "type": "address"},
                {"name": "receiver", "type": "address"},
                {"name": "sellAmount", "type": "uint256"},
                {"name": "buyAmount", "type": "uint256"},
                {"name": "validTo", "type": "uint32"},
                {"name": "appData", "type": "bytes32"},
                {"name": "feeAmount", "type": "uint256"},
                {"name": "kind", "type": "string"},
                {"name": "partiallyFillable", "type": "bool"},
                {"name": "sellTokenBalance", "type": "string"},
                {"name": "buyTokenBalance", "type": "string"},
            ],
        },
        "primaryType": "Order",
        "domain": {
            "name": "Gnosis Protocol",
            "version": "v2",
            "chainId": 11155111,
            "verifyingContract": "0x9008D19f58AAbD9eD0D60971565AA8510560ab41",
        },
        "message": {
            "sellToken": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
            "buyToken": "0xbe72E441BF55620febc26715db68d3494213D8Cb",
            "receiver": "0xYOUR_ADDRESS",
            "sellAmount": 1000000000000000000,
            "buyAmount": 190224099,
            "validTo": 1741700000,
            "appData": bytes.fromhex(
                "0000000000000000000000000000000000000000000000000000000000000000"
            ),
            "feeAmount": 0,
            "kind": "sell",
            "partiallyFillable": False,
            "sellTokenBalance": "erc20",
            "buyTokenBalance": "erc20",
        },
    }
    
    msg = encode_structured_data(order_typed_data)
    signed = Account.sign_message(msg, private_key="0xYOUR_PRIVATE_KEY")
    print(signed.signature.hex())
    
    30
    Install dependencies with: pip install eth-account
    31
    Option C: Sign with TypeScript
    32
    import { ethers } from "ethers";
    
    const domain = {
      name: "Gnosis Protocol",
      version: "v2",
      chainId: 11155111,
      verifyingContract: "0x9008D19f58AAbD9eD0D60971565AA8510560ab41",
    };
    
    const types = {
      Order: [
        { name: "sellToken", type: "address" },
        { name: "buyToken", type: "address" },
        { name: "receiver", type: "address" },
        { name: "sellAmount", type: "uint256" },
        { name: "buyAmount", type: "uint256" },
        { name: "validTo", type: "uint32" },
        { name: "appData", type: "bytes32" },
        { name: "feeAmount", type: "uint256" },
        { name: "kind", type: "string" },
        { name: "partiallyFillable", type: "bool" },
        { name: "sellTokenBalance", type: "string" },
        { name: "buyTokenBalance", type: "string" },
      ],
    };
    
    const order = {
      sellToken: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
      buyToken: "0xbe72E441BF55620febc26715db68d3494213D8Cb",
      receiver: "0xYOUR_ADDRESS",
      sellAmount: "1000000000000000000",
      buyAmount: "190224099",
      validTo: 1741700000,
      appData:
        "0x0000000000000000000000000000000000000000000000000000000000000000",
      feeAmount: "0",
      kind: "sell",
      partiallyFillable: false,
      sellTokenBalance: "erc20",
      buyTokenBalance: "erc20",
    };
    
    const wallet = new ethers.Wallet("0xYOUR_PRIVATE_KEY");
    const signature = await wallet.signTypedData(domain, types, order);
    console.log(signature);
    
    33
    Install dependencies with: npm install ethers
    34
    Submit the Order
    35
    Once you have the signature, submit the signed order to the order book:
    36
    curl -X POST "https://api.cow.fi/sepolia/api/v1/orders" \
      -H "Content-Type: application/json" \
      -d '{
        "sellToken": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
        "buyToken": "0xbe72E441BF55620febc26715db68d3494213D8Cb",
        "sellAmount": "1000000000000000000",
        "buyAmount": "190224099",
        "validTo": 1741700000,
        "feeAmount": "0",
        "kind": "sell",
        "partiallyFillable": false,
        "receiver": "0xYOUR_ADDRESS",
        "from": "0xYOUR_ADDRESS",
        "appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "signingScheme": "eip712",
        "signature": "0xYOUR_SIGNATURE",
        "sellTokenBalance": "erc20",
        "buyTokenBalance": "erc20"
      }'
    
    37
    A successful response returns the order UID as a plain string:
    38
    "0x2a3f1c9e8b7d6a5e4f3c2b1a09d8e7f6c5b4a3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2"
    
    39
    Save this UID — you will need it to monitor and (optionally) cancel the order.
    40
    Monitor the Order
    41
    Poll the order status to track its lifecycle:
    42
    # Check order status
    curl "https://api.cow.fi/sepolia/api/v1/orders/ORDER_UID"
    
    43
    The status field in the response will be one of:
    44
    StatusMeaningopenOrder is in the book and awaiting solver executionfulfilledOrder has been fully settled on-chainexpiredThe validTo timestamp has passed without settlementcancelledOrder was cancelled by the owner
    45
    To check whether any trades have been executed for your order:
    46
    # Check trades for this order
    curl "https://api.cow.fi/sepolia/api/v1/trades?orderUid=ORDER_UID"
    
    47
    This returns an array of trade objects. Each trade includes the on-chain txHash, the actual sellAmount and buyAmount that were settled, and the block number.
    48
    Cancel an Order (Optional)
    49
    If your order is still open and you want to cancel it, you need to sign a cancellation message and send a DELETE request:
    50
    curl -X DELETE "https://api.cow.fi/sepolia/api/v1/orders/ORDER_UID" \
      -H "Content-Type: application/json" \
      -d '{
        "signature": "0xCANCEL_SIGNATURE",
        "signingScheme": "eip712"
      }'
    
    51
    The cancellation signature is an EIP-712 signature over the order UID bytes using the same signing key that created the order.
    52
    Cancellation is best-effort. If a solver has already included your order in a solution that is being settled, the cancellation may not take effect in time.

    Troubleshooting

    Common errors you may encounter:
    ErrorCauseFix
    InsufficientBalanceYour wallet does not hold enough sell tokenFund your wallet with more tokens on Sepolia
    InsufficientAllowanceThe VaultRelayer has not been approved to spend your tokenApprove 0xC92E8bdf79f0507f65a392b0ab4667716BFE0110 for the sell token
    InvalidSignatureThe signature does not match the order data or signerEnsure the EIP-712 domain, types, and order values match exactly
    DuplicateOrderAn identical order already existsChange validTo or amounts slightly
    SellAmountDoesNotCoverFeeThe sell amount is too small to cover network costsIncrease your sell amount
    QuoteNotFound / NoLiquidityNo solver can fill this trade pairTry a different token pair or larger amount
    For the full list of API error codes, see the Order Book API Reference.

    Next Steps

    Now that you know how the raw API works, explore these resources:
    Last modified on March 12, 2026