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

# Requesters

> Request Combo quotes and execute accepted trades

Use the Requester API to request executable Combo quotes, accept a winning
quote, and track the trade through onchain execution. This guide walks through
the complete workflow.

> [!WARNING]
>
> Keep the CLOB API secret and passphrase on trusted infrastructure. Never
> expose them in a browser or other untrusted client.

## Request and Execute a Quote

> [tab] API
>
> The production gateway is
> `https://combos-rfq-gateway-requester-api.polymarket.com`, with base path
> `/v1/requester/rfq`.
>
> **1. Prepare the Trading Account**
>
> Create a Polymarket account, obtain its [CLOB API
> credentials](https://docs.polymarket.com/getting-started/api.md#authentication), and fund the account wallet.
> Set up the wallet and trading approvals by following [Wallets and
> Authentication](https://docs.polymarket.com/trading/wallets-auth.md).
>
> Resolve the CLOB credential address, order signer, order maker, and signature
> type for the account's wallet:
>
> | Wallet type | `signature_type` | `POLY_ADDRESS` | `signer_address` | `maker_address` |
> | - | - | - | - | - |
> | Deposit Wallet | `3` POLY\_1271 | Account or session signer with CLOB credentials | Deposit Wallet address | Deposit Wallet address |
> | Proxy Wallet | `1` Proxy | Account signer with CLOB credentials | Account signer | Proxy Wallet address |
> | Safe Wallet | `2` Safe | Account signer with CLOB credentials | Account signer | Safe Wallet address |
> | EOA | `0` EOA | Not supported | Not supported | Not supported |
>
> Both requester gateways reject EOA accounts. The Requester API returns
> `UNSUPPORTED_REQUESTER_SIGNATURE_TYPE`; use a Deposit, Proxy, or Safe Wallet.
>
> **2. Sign Request Headers**
>
> Every request requires these CLOB L2 headers:
>
> * `POLY_ADDRESS`
> * `POLY_API_KEY`
> * `POLY_PASSPHRASE`
> * `POLY_TIMESTAMP`
> * `POLY_SIGNATURE`
>
> `POLY_ADDRESS` is the address associated with the CLOB API credentials. Use the
> wallet identity values resolved in the previous step for the request body and
> signed order.
>
> Compute `POLY_SIGNATURE` with HMAC-SHA256, using the base64-decoded CLOB secret
> as the key. Sign an uppercase method, a Unix timestamp in seconds, the full
> request path without the host or query string, and the exact serialized body
> sent on the wire. Omit the body for `GET` requests. Encode the digest as
> base64url with padding preserved.
>
> ```text theme={null}
> HMAC-SHA256(
>   base64Decode(secret),
>   timestamp + HTTP_METHOD + FULL_REQUEST_PATH + REQUEST_BODY
> )
> ```
>
> Generate a fresh timestamp and signature for every request, including each
> status poll.
>
> **3. Choose the Combo Legs**
>
> Fetch active Combo-enabled markets from the public catalog:
>
> ```bash theme={null}
> curl -G "https://combos-rfq-api.polymarket.com/v1/rfq/combo-markets" \
>   --data-urlencode "limit=50"
> ```
>
> The response contains a `markets` array and an opaque `next_cursor`. Pass the
> cursor back as `cursor` to fetch the next page. For each market,
> `position_ids`, `outcomes`, and `outcome_prices` are aligned by array index:
> index `0` is YES and index `1` is NO.
>
> Choose 2–50 unique, mutually compatible outcome position IDs and pass them in
> `leg_position_ids`.
>
> **4. Create the RFQ**
>
> Choose the request direction and size:
>
> * A BUY sets the maximum collateral budget, including fees. Use
>   `unit: "notional"`.
> * A SELL specifies the number of Combo shares to sell. Use `unit: "shares"`.
>
> `value_e6` is a string in 6-decimal base units, and `side` must currently be
> `"YES"`. The gateway holds the request until the quote competition finishes.
> The examples below use a Deposit Wallet. The API accepts at most 15 create
> requests per rolling minute for each `maker_address`; requests above that limit
> return HTTP `429` with `RATE_LIMITED`.
>
> ```http BUY theme={null}
> POST /v1/requester/rfq/requests
> Content-Type: application/json
> POLY_ADDRESS: <clob-credential-address>
> POLY_API_KEY: <clob-api-key>
> POLY_PASSPHRASE: <clob-passphrase>
> POLY_TIMESTAMP: <unix-seconds>
> POLY_SIGNATURE: <l2-signature>
>
> {
>   "signer_address": "<deposit-wallet-address>",
>   "maker_address": "<deposit-wallet-address>",
>   "signature_type": 3,
>   "leg_position_ids": ["<yes-position-id-1>", "<yes-position-id-2>"],
>   "direction": "BUY",
>   "side": "YES",
>   "requested_size": {
>     "unit": "notional",
>     "value_e6": "1000000"
>   }
> }
> ```
>
> ```http SELL theme={null}
> POST /v1/requester/rfq/requests
> Content-Type: application/json
> POLY_ADDRESS: <clob-credential-address>
> POLY_API_KEY: <clob-api-key>
> POLY_PASSPHRASE: <clob-passphrase>
> POLY_TIMESTAMP: <unix-seconds>
> POLY_SIGNATURE: <l2-signature>
>
> {
>   "signer_address": "<deposit-wallet-address>",
>   "maker_address": "<deposit-wallet-address>",
>   "signature_type": 3,
>   "leg_position_ids": ["<yes-position-id-1>", "<yes-position-id-2>"],
>   "direction": "SELL",
>   "side": "YES",
>   "requested_size": {
>     "unit": "shares",
>     "value_e6": "1000000"
>   }
> }
> ```
>
> An executable response includes the server-owned RFQ ID, winning quote, and
> acceptance deadline:
>
> > [accordion] Create RFQ Response
> >
> > ```json theme={null}
> > {
> >   "rfq_id": "<rfq-id>",
> >   "status": "AWAITING_REQUESTER_ACCEPTANCE",
> >   "expires_at": 1773890763000,
> >   "request": {
> >     "rfq_id": "<rfq-id>",
> >     "maker_address": "<deposit-wallet-address>",
> >     "requestor_public_id": "<requester-public-id>",
> >     "leg_position_ids": ["<yes-position-id-1>", "<yes-position-id-2>"],
> >     "condition_id": "<combo-condition-id>",
> >     "yes_position_id": "<combo-yes-position-id>",
> >     "no_position_id": "<combo-no-position-id>",
> >     "direction": "BUY",
> >     "side": "YES",
> >     "requested_size": {
> >       "unit": "notional",
> >       "value_e6": "1000000"
> >     },
> >     "created_at": 1773890758000
> >   },
> >   "quote": {
> >     "quote_id": "<quote-id>",
> >     "blended_price_e6": "500000",
> >     "maker_amount_e6": "966191",
> >     "taker_amount_e6": "1932381",
> >     "total_required_e6": "1000000",
> >     "net_receive_e6": "1932381"
> >   }
> > }
> > ```
>
> `expires_at` and `request.created_at` are Unix timestamps in milliseconds. The
> acceptance window is five seconds from quote readiness. Sign and accept the
> order before `expires_at`. `total_required_e6` is the exact balance required:
> collateral including fees for BUY, or Combo shares for SELL. For BUY,
> `net_receive_e6` is the Combo shares received. For SELL, it is the exact
> collateral proceeds after fees.
>
> If no usable quote is available, the gateway returns HTTP `200` with a terminal
> business outcome:
>
> ```json theme={null}
> {
>   "rfq_id": "<rfq-id>",
>   "status": "FAILED",
>   "error": {
>     "code": "NO_QUOTES",
>     "message": "no quotes"
>   }
> }
> ```
>
> > [!WARNING]
> >
> > A local create timeout or lost connection has an unknown outcome. The RFQ may
> > have been created even if you never received its server-owned ID. This API has
> > no idempotency key or lookup for that case, and retrying may create another
> > RFQ.
>
> **5. Build and Sign the Requester Order**
>
> Build an Exchange v3 order from the returned request and quote. Use Polygon
> chain ID `137` and Exchange v3 contract
> `0xe3333700cA9d93003F00f0F71f8515005F6c00Aa` for the EIP-712 domain.
>
> For both directions, copy `maker_amount_e6` to `makerAmount` and
> `taker_amount_e6` to `takerAmount`, and use the returned Combo YES position ID
> as `tokenId`. Set `side` to `0` for BUY or `1` for SELL.
>
> The `builder` field must be the zero bytes32 value. A non-zero value is rejected
> with `BUILDER_ATTRIBUTION_NOT_ALLOWED`. This gateway does not support Builder
> attribution. Approved builders that need orders attributed to their Builder
> code must authenticate and submit them through the [Builder Gateway
> workflow](https://docs.polymarket.com/trading/combos/builders.md#request-and-execute-a-quote) instead.
>
> Use the same wallet identity selected when creating the RFQ. The wallet type
> determines which payload to sign and how to encode `signed_order.signature`:
>
> | Wallet type | `signatureType` | Payload to sign | Submitted signature |
> | - | - | - | - |
> | Deposit Wallet | `3` | `depositWalletTypedData` | ERC-7739-wrapped signature |
> | Proxy Wallet | `1` | `exchangeV3OrderTypedData` | Standard 65-byte EVM signature |
> | Safe Wallet | `2` | `exchangeV3OrderTypedData` | Standard 65-byte EVM signature |
>
> For a Deposit Wallet, wrap the Exchange v3 order in the Deposit Wallet's
> `TypedDataSign` structure. Both `maker` and `signer` in `contents` are the
> Deposit Wallet address. The account or session signer signs this outer payload.
>
> ```json Deposit Wallet Typed Data theme={null}
> {
>   "domain": {
>     "name": "Polymarket CTF Exchange",
>     "version": "3",
>     "chainId": 137,
>     "verifyingContract": "0xe3333700cA9d93003F00f0F71f8515005F6c00Aa"
>   },
>   "types": {
>     "Order": [
>       { "name": "salt", "type": "uint256" },
>       { "name": "maker", "type": "address" },
>       { "name": "signer", "type": "address" },
>       { "name": "tokenId", "type": "uint256" },
>       { "name": "makerAmount", "type": "uint256" },
>       { "name": "takerAmount", "type": "uint256" },
>       { "name": "side", "type": "uint8" },
>       { "name": "signatureType", "type": "uint8" },
>       { "name": "timestamp", "type": "uint256" },
>       { "name": "metadata", "type": "bytes32" },
>       { "name": "builder", "type": "bytes32" }
>     ],
>     "TypedDataSign": [
>       { "name": "contents", "type": "Order" },
>       { "name": "name", "type": "string" },
>       { "name": "version", "type": "string" },
>       { "name": "chainId", "type": "uint256" },
>       { "name": "verifyingContract", "type": "address" },
>       { "name": "salt", "type": "bytes32" }
>     ]
>   },
>   "primaryType": "TypedDataSign",
>   "message": {
>     "contents": {
>       "salt": "<salt>",
>       "maker": "<deposit-wallet-address>",
>       "signer": "<deposit-wallet-address>",
>       "tokenId": "<combo-yes-position-id>",
>       "makerAmount": "966191",
>       "takerAmount": "1932381",
>       "side": 0,
>       "signatureType": 3,
>       "timestamp": "<unix-seconds>",
>       "metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
>       "builder": "0x0000000000000000000000000000000000000000000000000000000000000000"
>     },
>     "name": "DepositWallet",
>     "version": "1",
>     "chainId": 137,
>     "verifyingContract": "<deposit-wallet-address>",
>     "salt": "0x0000000000000000000000000000000000000000000000000000000000000000"
>   }
> }
> ```
>
> For a Proxy or Safe Wallet, sign the Exchange v3 `Order` directly. Set `maker`
> to the Proxy or Safe Wallet address, `signer` to the account signer, and
> `signatureType` to `1` or `2`, respectively.
>
> ```json Proxy or Safe Wallet Typed Data theme={null}
> {
>   "domain": {
>     "name": "Polymarket CTF Exchange",
>     "version": "3",
>     "chainId": 137,
>     "verifyingContract": "0xe3333700cA9d93003F00f0F71f8515005F6c00Aa"
>   },
>   "types": {
>     "EIP712Domain": [
>       { "name": "name", "type": "string" },
>       { "name": "version", "type": "string" },
>       { "name": "chainId", "type": "uint256" },
>       { "name": "verifyingContract", "type": "address" }
>     ],
>     "Order": [
>       { "name": "salt", "type": "uint256" },
>       { "name": "maker", "type": "address" },
>       { "name": "signer", "type": "address" },
>       { "name": "tokenId", "type": "uint256" },
>       { "name": "makerAmount", "type": "uint256" },
>       { "name": "takerAmount", "type": "uint256" },
>       { "name": "side", "type": "uint8" },
>       { "name": "signatureType", "type": "uint8" },
>       { "name": "timestamp", "type": "uint256" },
>       { "name": "metadata", "type": "bytes32" },
>       { "name": "builder", "type": "bytes32" }
>     ]
>   },
>   "primaryType": "Order",
>   "message": {
>     "salt": "<salt>",
>     "maker": "<proxy-or-safe-wallet-address>",
>     "signer": "<account-signer-address>",
>     "tokenId": "<combo-yes-position-id>",
>     "makerAmount": "966191",
>     "takerAmount": "1932381",
>     "side": 0,
>     "signatureType": 1,
>     "timestamp": "<unix-seconds>",
>     "metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
>     "builder": "0x0000000000000000000000000000000000000000000000000000000000000000"
>   }
> }
> ```
>
> Sign the selected payload with the account or session signer. Deposit Wallets
> must wrap the raw signature for ERC-7739 validation; Proxy and Safe Wallets
> submit the signature returned by signing the Exchange `Order` directly.
>
> ```js Deposit Wallet theme={null}
> import { privateKeyToAccount } from "viem/accounts";
>
> const signer = privateKeyToAccount(process.env.SIGNER_PRIVATE_KEY);
> const innerSignature = await signer.signTypedData(depositWalletTypedData);
> const signature = wrapDepositWalletSignature(
>   depositWalletTypedData,
>   innerSignature,
> );
> ```
>
> ```js Proxy or Safe Wallet theme={null}
> import { privateKeyToAccount } from "viem/accounts";
>
> const signer = privateKeyToAccount(process.env.SIGNER_PRIVATE_KEY);
> const signature = await signer.signTypedData(exchangeV3OrderTypedData);
> ```
>
> ```js wrapDepositWalletSignature() theme={null}
> import { concatHex, encodeAbiParameters, keccak256, toHex } from "viem";
>
> const ORDER_TYPE =
>   "Order(uint256 salt,address maker,address signer,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint8 side,uint8 signatureType,uint256 timestamp,bytes32 metadata,bytes32 builder)";
> const EIP712_DOMAIN_TYPE =
>   "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)";
>
> function wrapDepositWalletSignature(typedData, innerSignature) {
>   const order = typedData.message.contents;
>   const exchangeDomain = typedData.domain;
>
>   const appDomainSeparator = keccak256(
>     encodeAbiParameters(
>       [
>         { type: "bytes32" },
>         { type: "bytes32" },
>         { type: "bytes32" },
>         { type: "uint256" },
>         { type: "address" },
>       ],
>       [
>         keccak256(toHex(EIP712_DOMAIN_TYPE)),
>         keccak256(toHex(exchangeDomain.name)),
>         keccak256(toHex(exchangeDomain.version)),
>         BigInt(exchangeDomain.chainId),
>         exchangeDomain.verifyingContract,
>       ],
>     ),
>   );
>   const contentsHash = keccak256(
>     encodeAbiParameters(
>       [
>         { type: "bytes32" },
>         { type: "uint256" },
>         { type: "address" },
>         { type: "address" },
>         { type: "uint256" },
>         { type: "uint256" },
>         { type: "uint256" },
>         { type: "uint8" },
>         { type: "uint8" },
>         { type: "uint256" },
>         { type: "bytes32" },
>         { type: "bytes32" },
>       ],
>       [
>         keccak256(toHex(ORDER_TYPE)),
>         BigInt(order.salt),
>         order.maker,
>         order.signer,
>         BigInt(order.tokenId),
>         BigInt(order.makerAmount),
>         BigInt(order.takerAmount),
>         order.side,
>         order.signatureType,
>         BigInt(order.timestamp),
>         order.metadata,
>         order.builder,
>       ],
>     ),
>   );
>
>   return concatHex([
>     innerSignature,
>     appDomainSeparator,
>     contentsHash,
>     toHex(ORDER_TYPE),
>     toHex(ORDER_TYPE.length, { size: 2 }),
>   ]);
> }
> ```
>
> **6. Accept the Quote**
>
> Submit the signed order with the winning `quote_id` before the returned
> `expires_at` deadline.
>
> ```http theme={null}
> POST /v1/requester/rfq/requests/<rfq-id>/accept
> Content-Type: application/json
> POLY_ADDRESS: <clob-credential-address>
> POLY_API_KEY: <clob-api-key>
> POLY_PASSPHRASE: <clob-passphrase>
> POLY_TIMESTAMP: <unix-seconds>
> POLY_SIGNATURE: <l2-signature>
>
> {
>   "quote_id": "<quote-id>",
>   "signed_order": {
>     "salt": "<salt>",
>     "maker": "<deposit-wallet-address>",
>     "signer": "<deposit-wallet-address>",
>     "tokenId": "<combo-yes-position-id>",
>     "makerAmount": "966191",
>     "takerAmount": "1932381",
>     "side": 0,
>     "signatureType": 3,
>     "timestamp": "<unix-seconds>",
>     "metadata": "0x0000000000000000000000000000000000000000000000000000000000000000",
>     "builder": "0x0000000000000000000000000000000000000000000000000000000000000000",
>     "signature": "<order-signature>"
>   }
> }
> ```
>
> The response contains the latest known state. If Last Look is pending, the
> gateway waits up to one second for an execution or terminal update before
> returning.
>
> ```json theme={null}
> {
>   "rfq_id": "<rfq-id>",
>   "status": "EXECUTING",
>   "taker_order_hash": "<order-hash>"
> }
> ```
>
> `EXECUTING` is not a confirmed fill. A response may instead remain
> `AWAITING_MAKER_CONFIRMATION`, or return HTTP `200` with `status: "FAILED"` and
> an `error` object if the maker declines or execution fails. Retrying the same
> authenticated acceptance does not execute the order twice; use `rfq_id` as the
> stable recovery identifier.
>
> **7. Poll Status**
>
> After acceptance, fetch durable status with a newly signed L2 request.
>
> ```http theme={null}
> GET /v1/requester/rfq/requests/<rfq-id>
> Accept: application/json
> POLY_ADDRESS: <clob-credential-address>
> POLY_API_KEY: <clob-api-key>
> POLY_PASSPHRASE: <clob-passphrase>
> POLY_TIMESTAMP: <unix-seconds>
> POLY_SIGNATURE: <l2-signature>
> ```
>
> ```json theme={null}
> {
>   "rfq_id": "<rfq-id>",
>   "status": "FILLED",
>   "tx_hash": "<transaction-hash>"
> }
> ```
>
> Status reads before acceptance return HTTP `409`. Poll while the state is
> `AWAITING_MAKER_CONFIRMATION`, `EXECUTING`, `MINED`, or `RETRYING`. Stop on a
> successful `CONFIRMED` or `FILLED` state, or a terminal `FAILED`, `EXPIRED`, or
> `CANCELED` state. A local polling timeout does not mean the trade failed; resume
> polling the same `rfq_id`.

> [tab] TypeScript
>
> > [!NOTE]
> >
> > TypeScript SDK support is coming soon. Use the API workflow in the API tab for
> > now.

> [tab] Python
>
> > [!NOTE]
> >
> > Python SDK support is coming soon. Use the API workflow in the API tab for
> > now.

## Handle Errors

Validation and dependency failures use non-`200` HTTP statuses with stable
string codes:

```json theme={null}
{
  "error": "invalid acceptance",
  "code": "INVALID_ACCEPTANCE"
}
```

| HTTP status | Common codes |
| - | - |
| `400` | `INVALID_JSON`, `INVALID_RFQ`, `INVALID_IDENTITY`, `INVALID_ACCEPTANCE`, `INVALID_SIGNATURE`, `UNSUPPORTED_REQUESTER_SIGNATURE_TYPE`, `BUILDER_ATTRIBUTION_NOT_ALLOWED` |
| `401` | `UNAUTHENTICATED` |
| `403` | `ADDRESS_MISMATCH`, `REQUEST_FAILED` |
| `404` | `UNKNOWN_RFQ` |
| `409` | `REQUEST_FAILED`, `QUOTE_MISMATCH`, `EXPIRED_RFQ`, `INVALID_RFQ_STATE` |
| `429` | `RATE_LIMITED` |
| `503` | `SERVICE_UNAVAILABLE`, `PRE_EXECUTION_BALANCE_RESERVATION_FAILED`, `TRADE_SUBMISSION_FAILED` |

Treat codes as strings so integrations remain compatible with new codes. HTTP
`200` can still contain a failed business outcome; always inspect both `status`
and the nested `error` object.


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