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

# Quickstart: Python

> Place your first CoW Protocol order using Python — from quote to confirmed trade in under 10 minutes.

export const InlineResource = ({value, copyValue, href, chain, explorerHref, explorerType = "address", explorerSuffix = "", showCopy = true, showExplorer = false, openInNewTab}) => {
  const explorerBases = {
    arbitrum: "https://arbiscan.io",
    arbitrum_one: "https://arbiscan.io",
    avalanche: "https://snowscan.xyz",
    base: "https://basescan.org",
    bnb: "https://bscscan.com",
    ethereum: "https://etherscan.io",
    gnosis: "https://gnosisscan.io",
    ink: "https://explorer.inkonchain.com",
    linea: "https://lineascan.build",
    mainnet: "https://etherscan.io",
    optimism: "https://optimistic.etherscan.io",
    plasma: "https://plasmascan.to",
    polygon: "https://polygonscan.com",
    sepolia: "https://sepolia.etherscan.io",
    xdai: "https://gnosisscan.io"
  };
  const isExternalUrl = (candidate = "") => (/^https?:\/\//i).test(candidate);
  const isHexAddress = (candidate = "") => (/^0x[a-fA-F0-9]{40}$/).test(candidate);
  const normalizeUrl = (candidate = "") => {
    if (!candidate) {
      return "";
    }
    if (candidate.startsWith("/") || candidate.startsWith("#") || isExternalUrl(candidate)) {
      return candidate;
    }
    return `https://${candidate}`;
  };
  const compactUrl = (candidate = "") => {
    const withoutProtocol = candidate.replace(/^https?:\/\//i, "");
    if (withoutProtocol.length <= 40) {
      return withoutProtocol;
    }
    const [host, ...segments] = withoutProtocol.split("/");
    if (!segments.length) {
      return `${withoutProtocol.slice(0, 20)}...${withoutProtocol.slice(-12)}`;
    }
    const tail = segments.length >= 2 ? `${segments[segments.length - 2]}/${segments[segments.length - 1]}` : segments[segments.length - 1];
    return `${host}/.../${tail}`;
  };
  const resolvedExplorerHref = (() => {
    if (explorerHref) {
      return explorerHref;
    }
    if (!chain || !value) {
      return "";
    }
    const base = explorerBases[chain];
    if (!base) {
      return "";
    }
    return `${base}/${explorerType}/${value}${explorerSuffix}`;
  })();
  const resolvedHref = normalizeUrl(href || "");
  const primaryHref = resolvedHref || (showExplorer ? resolvedExplorerHref : "");
  const primaryTarget = openInNewTab ?? (primaryHref ? isExternalUrl(primaryHref) : false);
  const shouldCompactValue = isExternalUrl(value || "") || isExternalUrl(primaryHref);
  const displayValue = shouldCompactValue ? compactUrl(value) : value;
  const contentClassName = ["inline-resource__value", shouldCompactValue ? "inline-resource__value--compact" : "", isHexAddress(value || "") ? "inline-resource__value--address" : ""].filter(Boolean).join(" ");
  const setButtonState = (button, state, label) => {
    if (!button) {
      return;
    }
    button.dataset.state = state;
    button.setAttribute("aria-label", label);
    button.title = label;
    if (button.__copyTimer) {
      window.clearTimeout(button.__copyTimer);
      button.__copyTimer = null;
    }
    if (state === "idle") {
      return;
    }
    button.__copyTimer = window.setTimeout(() => {
      button.dataset.state = "idle";
      button.setAttribute("aria-label", "Copy value");
      button.title = "Copy value";
    }, 1200);
  };
  const fallbackCopy = text => {
    if (typeof document === "undefined") {
      return false;
    }
    const input = document.createElement("textarea");
    input.value = text;
    input.setAttribute("readonly", "");
    input.style.left = "-9999px";
    input.style.position = "absolute";
    document.body.appendChild(input);
    input.select();
    let copied = false;
    try {
      copied = document.execCommand("copy");
    } catch {
      copied = false;
    }
    document.body.removeChild(input);
    return copied;
  };
  const copyText = async (text, button) => {
    if (!text) {
      setButtonState(button, "copied", "Nothing to copy");
      return;
    }
    try {
      if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(text);
        setButtonState(button, "copied", "Copied");
        return;
      }
    } catch {}
    const copied = fallbackCopy(text);
    setButtonState(button, copied ? "copied" : "idle", copied ? "Copied" : "Copy failed");
  };
  const content = <code className={contentClassName} title={value}>
      {displayValue}
    </code>;
  return <span className="inline-resource">
      {primaryHref ? <a className="inline-resource__link" href={primaryHref} rel={primaryTarget ? "noreferrer noopener" : undefined} target={primaryTarget ? "_blank" : undefined} title={value}>
          {content}
        </a> : content}

      {showCopy || showExplorer && resolvedExplorerHref && resolvedExplorerHref !== primaryHref ? <span className="inline-resource__actions">
          {showCopy ? <button aria-label="Copy value" className="inline-resource__action" data-state="idle" onClick={event => copyText(copyValue || value, event.currentTarget)} title="Copy value" type="button">
              <span className="inline-resource__icon inline-resource__icon--copy">
                <CopyIcon />
              </span>
              <span className="inline-resource__icon inline-resource__icon--check">
                <CheckIcon />
              </span>
            </button> : null}

          {showExplorer && resolvedExplorerHref && resolvedExplorerHref !== primaryHref ? <a aria-label="Open in explorer" className="inline-resource__action" href={resolvedExplorerHref} rel="noreferrer noopener" target="_blank" title="Open in explorer">
              <span className="inline-resource__icon">
                <ExternalLinkIcon />
              </span>
            </a> : null}
        </span> : null}
    </span>;
};

This guide walks you through placing a real order on CoW Protocol using Python and the REST API directly. By the end, you will have submitted a signed intent on the Sepolia testnet and watched it get filled.

<Note>
  **Prefer a higher-level approach?** The [cow-py SDK](/cow-py/quickstart) wraps all of this into a single `swap_tokens()` call with built-in signing, quoting, and order management. This guide is for developers who want to understand the raw API mechanics or who can't use the SDK.
</Note>

## Prerequisites

Before starting, ensure you have:

* **Python 3.8+** installed
* A wallet with **test tokens on Sepolia** (get Sepolia ETH from a [faucet](https://sepoliafaucet.com/), then wrap it to WETH)
* The wallet's **private key** (never use a key that controls real funds for testing)

<Warning>
  This guide uses the **Sepolia testnet**. Never hardcode private keys in production code. Use environment variables or a secrets manager.
</Warning>

Sepolia API base URL: <InlineResource value="https://api.cow.fi/sepolia/api/v1" href="https://api.cow.fi/sepolia/api/v1" />

Sepolia RPC: <InlineResource value="https://rpc.sepolia.org" href="https://rpc.sepolia.org" />

Settlement contract: <InlineResource value="0x9008D19f58AAbD9eD0D60971565AA8510560ab41" chain="sepolia" showExplorer />

GPv2VaultRelayer: <InlineResource value="0xC92E8bdf79f0507f65a392b0ab4667716BFE0110" chain="sepolia" showExplorer />

## Installation

Install the required packages:

```bash theme={null}
pip install web3 requests eth-account
```

## Walkthrough

<Steps>
  ### Setup

  Configure your connection, wallet, and constants.

  ```python theme={null}
  import json
  import time
  import requests
  from eth_account import Account
  from eth_account.messages import encode_typed_data
  from web3 import Web3

  # --------------- Configuration ---------------
  PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"  # Replace with your Sepolia wallet key
  API_BASE = "https://api.cow.fi/sepolia/api/v1"
  CHAIN_ID = 11155111  # Sepolia

  # Contract addresses (same across all chains unless noted)
  SETTLEMENT_CONTRACT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
  VAULT_RELAYER = "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110"

  # Tokens on Sepolia
  SELL_TOKEN = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"  # WETH
  BUY_TOKEN = "0xbe72E441BF55620febc26715db68d3494213D8Cb"   # COW

  account = Account.from_key(PRIVATE_KEY)
  w3 = Web3(Web3.HTTPProvider("https://rpc.sepolia.org"))

  print(f"Wallet: {account.address}")
  print(f"Balance: {w3.eth.get_balance(account.address)} wei")
  ```

  <Note>
    You can use any Sepolia RPC endpoint — Alchemy, Infura, or the public `https://rpc.sepolia.org`.
  </Note>

  ### Approve tokens

  CoW Protocol's settlement contract does not pull tokens directly from your wallet. Instead, the **GPv2VaultRelayer** contract transfers tokens on your behalf. You must approve it to spend your sell token.

  If you have already approved the relayer for this token, you can skip this step.

  ```python theme={null}
  # Minimal ERC-20 ABI — only the approve function
  erc20_abi = [
      {
          "inputs": [
              {"name": "spender", "type": "address"},
              {"name": "amount", "type": "uint256"},
          ],
          "name": "approve",
          "outputs": [{"name": "", "type": "bool"}],
          "type": "function",
      }
  ]

  token = w3.eth.contract(
      address=Web3.to_checksum_address(SELL_TOKEN), abi=erc20_abi
  )

  tx = token.functions.approve(
      Web3.to_checksum_address(VAULT_RELAYER),
      2**256 - 1,  # max approval
  ).build_transaction(
      {
          "from": account.address,
          "nonce": w3.eth.get_transaction_count(account.address),
          "gas": 50_000,
          "gasPrice": w3.eth.gas_price,
      }
  )

  signed_tx = account.sign_transaction(tx)
  tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
  receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

  print(f"Approval tx: {tx_hash.hex()}")
  print(f"Status: {'success' if receipt.status == 1 else 'failed'}")
  ```

  <Warning>
    This issues an **unlimited approval** for simplicity. In production, approve only the amount you intend to trade.
  </Warning>

  ### Get a quote

  Send your trade intention to the `/quote` endpoint. The API returns estimated amounts and fee information.

  ```python theme={null}
  quote_request = {
      "sellToken": SELL_TOKEN,
      "buyToken": BUY_TOKEN,
      "sellAmountBeforeFee": str(10**17),  # 0.1 WETH
      "kind": "sell",
      "from": account.address,
      "receiver": account.address,
      "validFor": 1800,  # 30 minutes
      "signingScheme": "eip712",
      "appData": "{}",
      "appDataHash": "0xb48d38f93eaa084033fc5970bf96e559c33c4cdc07d889ab00b4d63f9590739d",
  }

  response = requests.post(f"{API_BASE}/quote", json=quote_request)
  response.raise_for_status()
  quote_data = response.json()

  quote = quote_data["quote"]
  quote_id = quote_data["id"]
  print(f"Sell amount (after network costs): {quote['sellAmount']}")
  print(f"Buy amount (estimated):            {quote['buyAmount']}")
  print(f"Fee amount (network costs):        {quote['feeAmount']}")
  print(f"Valid until:                        {quote['validTo']}")
  print(f"Quote expires at:                   {quote_data['expiration']}")
  ```

  <Note>
    The quote is valid for a limited time (controlled by `validFor`). Sign and submit the order before `quote_data["expiration"]`. If the backend later rejects the quote as stale or no longer valid, request a fresh quote and retry.
  </Note>

  ### Sign the order (EIP-712)

  CoW Protocol orders are **intents** — signed messages that authorize the protocol to execute the trade on your behalf. Orders are signed using the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data standard.

  ```python theme={null}
  # EIP-712 domain for CoW Protocol
  domain_data = {
      "name": "Gnosis Protocol",
      "version": "v2",
      "chainId": CHAIN_ID,
      "verifyingContract": SETTLEMENT_CONTRACT,
  }

  # Order type definition — must match the contract exactly
  order_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"},
      ]
  }

  # Build the order data from the quote
  # For sell orders: sellAmount = quote.sellAmount + quote.feeAmount (spot price)
  #                  buyAmount  = quote.buyAmount (minimum to receive)
  app_data_hash = quote.get("appDataHash", quote["appData"])

  order_data = {
      "sellToken": quote["sellToken"],
      "buyToken": quote["buyToken"],
      "receiver": quote["receiver"],
      "sellAmount": int(quote["sellAmount"]) + int(quote["feeAmount"]),
      "buyAmount": int(quote["buyAmount"]),
      "validTo": quote["validTo"],
      "appData": bytes.fromhex(app_data_hash[2:]),
      "feeAmount": 0,
      "kind": "sell",
      "partiallyFillable": False,
      "sellTokenBalance": "erc20",
      "buyTokenBalance": "erc20",
  }

  # Sign the typed data
  signable = encode_typed_data(domain_data, order_types, order_data)
  signed = account.sign_message(signable)
  signature = signed.signature.hex()

  print(f"Order signed successfully")
  print(f"Signature: 0x{signature[:16]}...")
  ```

  <Note>
    The `feeAmount` in the signed order is set to `0` and the `sellAmount` is set to the spot price amount (`quote.sellAmount + quote.feeAmount`). The settlement contract deducts network costs from the sell amount automatically. See [How Intents Are Formed](/cow-protocol/explanation/how-it-works/how-intents-are-formed) for details on the fee pipeline.
  </Note>

  ### Submit the order

  Post the signed order to the `/orders` endpoint. The API returns an **order UID** that you can use to track execution.

  ```python theme={null}
  submit_body = {
      "sellToken": quote["sellToken"],
      "buyToken": quote["buyToken"],
      "receiver": quote["receiver"],
      "sellAmount": str(int(quote["sellAmount"]) + int(quote["feeAmount"])),
      "buyAmount": quote["buyAmount"],
      "validTo": quote["validTo"],
      "appData": quote["appData"],
      "appDataHash": app_data_hash,
      "feeAmount": "0",
      "kind": "sell",
      "partiallyFillable": False,
      "sellTokenBalance": "erc20",
      "buyTokenBalance": "erc20",
      "signingScheme": "eip712",
      "signature": "0x" + signature,
      "from": account.address,
      "quoteId": quote_id,
  }

  response = requests.post(f"{API_BASE}/orders", json=submit_body)
  response.raise_for_status()
  order_uid = response.json()

  print(f"Order UID: {order_uid}")
  print(f"Track at:  https://explorer.cow.fi/sepolia/orders/{order_uid}")
  ```

  ### Monitor the order

  Poll the API to watch the order progress from `open` to `fulfilled`.

  ```python theme={null}
  print("Waiting for order to be filled...")

  for _ in range(60):  # poll for up to 5 minutes
      resp = requests.get(f"{API_BASE}/orders/{order_uid}")
      resp.raise_for_status()
      status = resp.json()["status"]
      print(f"  Status: {status}")

      if status in ("fulfilled", "expired", "cancelled"):
          break
      time.sleep(5)

  if status == "fulfilled":
      print("Order filled successfully!")
  elif status == "expired":
      print("Order expired before a solver could fill it. Try again with a fresh quote.")
  else:
      print(f"Final status: {status}")
  ```
</Steps>

<Note>
  Before placing orders in production, review the **[Pre-Flight Checklist](/cow-protocol/howto/integrate/api#pre-flight-checklist)** to avoid the most common integration mistakes.
</Note>

## Troubleshooting

| Error                            | Cause                                                        | Fix                                                                 |
| -------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------- |
| `InsufficientAllowance`          | The vault relayer is not approved to spend your sell token   | Run the approval step (Step 2)                                      |
| `InvalidSignature`               | EIP-712 domain or type definitions do not match the contract | Verify `domain_data` and `order_types` match exactly as shown above |
| `QuoteNotFound` / `InvalidQuote` | The quote is no longer valid for order creation              | Request a fresh quote and sign/submit immediately                   |
| `InsufficientBalance`            | Your wallet does not hold enough sell token                  | Get test tokens from a Sepolia faucet and wrap ETH to WETH          |
| `SellAmountDoesNotCoverFee`      | Trade amount is too small to cover network costs             | Increase the `sellAmountBeforeFee` in your quote request            |

<Note>
  For a full list of API error codes, see the [Order Book API Reference](/cow-protocol/reference/apis/orderbook).
</Note>

## Next Steps

Now that you have placed your first order, explore more of CoW Protocol:

* **[cow-py SDK Quickstart](/cow-py/quickstart)** — Use the Python SDK for a higher-level experience (`swap_tokens()` handles everything)
* **[TypeScript SDK Quickstart](/cow-sdk/quickstart)** — Use the official TypeScript SDK for frontend/Node.js integrations
* **[Limit Orders](/cow-swap/tutorials/limit)** — Place limit orders that execute at your target price
* **[TWAP Orders](/cow-swap/tutorials/twap)** — Split large orders over time with Time-Weighted Average Price
* **[API Integration Guide](/cow-protocol/howto/integrate/api)** — Complete step-by-step API reference with fee formulas and pre-flight checklist
* **[Testing Guide](/cow-protocol/howto/integrate/testing)** — How to test on Sepolia and graduate to production
* **[How Intents Are Formed](/cow-protocol/explanation/how-it-works/how-intents-are-formed)** — Understand the fee pipeline and amount calculations
* **[Signing Schemes](/cow-protocol/reference/core/signing-schemes)** — Learn about EIP-712, `ethSign`, and pre-signed orders
