Skip to main content
The RFQ (Request for Quote) system allows traders to negotiate large trades off the order book. This guide covers the complete RFQ workflow for both requesters and quoters.

Overview

RFQ trading involves two parties:
  • Requester: Creates a request for a specific trade
  • Quoter: Responds with a quote offering to fill the request
The RFQ flow:
  1. Requester creates an RFQ request
  2. Quoters submit quotes in response
  3. Requester accepts a quote
  4. Quoter approves the order
  5. Trade executes

Setup

The RFQ client is accessible via the rfq property:
import { ClobClient, Chain, ApiKeyCreds } from "@polymarket/clob-client";
import { ethers } from "ethers";

const wallet = new ethers.Wallet("your-private-key");
const host = "https://clob.polymarket.com";
const chainId = Chain.POLYGON;

const creds: ApiKeyCreds = {
  key: "your-api-key",
  secret: "your-secret",
  passphrase: "your-passphrase",
};

const clobClient = new ClobClient(host, chainId, wallet, creds);

// Access RFQ methods
const rfqClient = clobClient.rfq;
All RFQ methods require L2 authentication (API credentials).

Creating an RFQ Request

As a requester, create an RFQ request to signal your intent to trade:
import { Side } from "@polymarket/clob-client";

const tokenID = "71321045679252212594626385532706912750332728571942532289631379312455583992563";

// Create a request to BUY 100 shares at 0.50
const request = await rfqClient.createRfqRequest(
  {
    tokenID: tokenID,
    price: 0.50,      // Desired price per share
    side: Side.BUY,   // BUY or SELL
    size: 100,        // Number of shares
  },
  { tickSize: "0.01" }
);

if (request.error) {
  console.error("Request failed:", request.error);
} else {
  console.log("Request ID:", request.requestId);
}

Request Parameters

tokenID
string
required
The token ID you want to trade.
price
number
required
Your desired price per share (0.01 to 0.99).
side
Side
required
Side.BUY to buy shares, Side.SELL to sell shares.
size
number
required
Number of shares you want to trade.

Creating a Quote (Quoter Side)

As a quoter, respond to requests with your quotes:
const requestId = "550e8400-e29b-41d4-a716-446655440000";

// Create a quote to SELL 100 shares at 0.50 (responding to a BUY request)
const quote = await rfqClient.createRfqQuote(
  {
    requestId: requestId,
    tokenID: tokenID,
    price: 0.50,       // Your offered price
    side: Side.SELL,   // Opposite of request side
    size: 100,         // Must match or be less than request size
  },
  { tickSize: "0.01" }
);

if (quote.error) {
  console.error("Quote failed:", quote.error);
} else {
  console.log("Quote ID:", quote.quoteId);
}
When responding to a BUY request, quote with Side.SELL. When responding to a SELL request, quote with Side.BUY.

Viewing Requests

Get RFQ requests with optional filtering:
import { GetRfqRequestsParams } from "@polymarket/clob-client";

// Get all active requests
const requests = await rfqClient.getRfqRequests({
  state: "active",
});

console.log(`Found ${requests.data.length} active requests`);

for (const req of requests.data) {
  console.log(`Request ${req.requestId}:`);
  console.log(`  ${req.side} ${req.sizeIn} @ ${req.price}`);
  console.log(`  Expires: ${req.expiry}`);
}

Filtering Options

const params: GetRfqRequestsParams = {
  state: "active",              // "active" or "inactive"
  requestIds: ["uuid1", "uuid2"],  // Filter by specific IDs
  markets: ["0x5f65..."],       // Filter by market condition ID
  sizeMin: 50,                  // Minimum token size
  sizeMax: 200,                 // Maximum token size
  priceMin: 0.40,               // Minimum price
  priceMax: 0.60,               // Maximum price
  sortBy: "price",              // "price", "expiry", "size", or "created"
  sortDir: "asc",               // "asc" or "desc"
  offset: "MA==",               // Pagination cursor
  limit: 50,                    // Results per page (max 100)
};

const filtered = await rfqClient.getRfqRequests(params);

Viewing Quotes

Quoters and requesters see different quotes:

As a Requester (Quotes on Your Requests)

// Get quotes that others have made on your requests
const quotes = await rfqClient.getRfqRequesterQuotes({
  state: "active",
  requestIds: [requestId],
});

for (const quote of quotes.data) {
  console.log(`Quote ${quote.quoteId}:`);
  console.log(`  ${quote.side} ${quote.sizeOut} @ ${quote.price}`);
  console.log(`  Match type: ${quote.matchType}`);
}

As a Quoter (Your Quotes on Others’ Requests)

// Get quotes you have made on others' requests
const myQuotes = await rfqClient.getRfqQuoterQuotes({
  state: "active",
});

for (const quote of myQuotes.data) {
  console.log(`Your quote ${quote.quoteId} on request ${quote.requestId}`);
}

Get Best Quote

Find the best quote for a specific request:
const bestQuote = await rfqClient.getRfqBestQuote({
  requestId: requestId,
});

console.log("Best quote:", bestQuote.quoteId);
console.log(`Price: ${bestQuote.price}`);

Accepting a Quote (Requester)

Once you find a quote you like, accept it:
const expiration = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now

const result = await rfqClient.acceptRfqQuote({
  requestId: requestId,
  quoteId: quoteId,
  expiration: expiration,
});

console.log("Quote accepted!");
Accepting a quote:
  1. Creates a signed order automatically
  2. Submits the acceptance to the server
  3. Notifies the quoter

Approving an Order (Quoter)

After a quote is accepted, the quoter must approve:
const expiration = Math.floor(Date.now() / 1000) + 3600;

const result = await rfqClient.approveRfqOrder({
  requestId: requestId,
  quoteId: quoteId,
  expiration: expiration,
});

console.log("Order approved and executed!");

Canceling Requests and Quotes

Cancel a Request

await rfqClient.cancelRfqRequest({ requestId: requestId });
console.log("Request canceled");

Cancel a Quote

await rfqClient.cancelRfqQuote({ quoteId: quoteId });
console.log("Quote canceled");

Complete RFQ Flow Example

Here’s a complete example showing both sides:
import { ClobClient, Chain, Side, ApiKeyCreds } from "@polymarket/clob-client";
import { ethers } from "ethers";

const host = "https://clob.polymarket.com";
const chainId = Chain.POLYGON;
const tokenID = "71321045679252212594626385532706912750332728571942532289631379312455583992563";

// ========================================
// REQUESTER SIDE
// ========================================
const requesterWallet = new ethers.Wallet(process.env.REQUESTER_PK!);
const requesterCreds: ApiKeyCreds = {
  key: process.env.REQUESTER_API_KEY!,
  secret: process.env.REQUESTER_SECRET!,
  passphrase: process.env.REQUESTER_PASS_PHRASE!,
};

const requesterClient = new ClobClient(
  host,
  chainId,
  requesterWallet,
  requesterCreds
);

// Step 1: Create request
console.log("[Requester] Creating RFQ request...");
const request = await requesterClient.rfq.createRfqRequest(
  {
    tokenID: tokenID,
    price: 0.50,
    side: Side.BUY,
    size: 100,
  },
  { tickSize: "0.01" }
);

if (request.error) {
  throw new Error(`Request failed: ${request.error}`);
}

const requestId = request.requestId;
console.log(`[Requester] Request created: ${requestId}`);

// ========================================
// QUOTER SIDE
// ========================================
const quoterWallet = new ethers.Wallet(process.env.QUOTER_PK!);
const quoterCreds: ApiKeyCreds = {
  key: process.env.QUOTER_API_KEY!,
  secret: process.env.QUOTER_SECRET!,
  passphrase: process.env.QUOTER_PASS_PHRASE!,
};

const quoterClient = new ClobClient(
  host,
  chainId,
  quoterWallet,
  quoterCreds
);

// Step 2: Create quote
console.log("[Quoter] Creating quote...");
const quote = await quoterClient.rfq.createRfqQuote(
  {
    requestId: requestId,
    tokenID: tokenID,
    price: 0.50,
    side: Side.SELL,  // Opposite of request side
    size: 100,
  },
  { tickSize: "0.01" }
);

if (quote.error) {
  throw new Error(`Quote failed: ${quote.error}`);
}

const quoteId = quote.quoteId;
console.log(`[Quoter] Quote created: ${quoteId}`);

// ========================================
// REQUESTER ACCEPTS
// ========================================
console.log("[Requester] Accepting quote...");
const expiration = Math.floor(Date.now() / 1000) + 3600;

await requesterClient.rfq.acceptRfqQuote({
  requestId: requestId,
  quoteId: quoteId,
  expiration: expiration,
});

console.log("[Requester] Quote accepted!");

// ========================================
// QUOTER APPROVES
// ========================================
console.log("[Quoter] Approving order...");

await quoterClient.rfq.approveRfqOrder({
  requestId: requestId,
  quoteId: quoteId,
  expiration: expiration,
});

console.log("[Quoter] Order approved! Trade executed.");

Match Types

Quotes can have different match types:
  • COMPLEMENTARY: Direct BUY ↔ SELL match (most common)
  • MINT: Both sides BUY, complementary tokens are minted
  • MERGE: Both sides SELL, complementary tokens are merged
The SDK handles match type logic automatically.

RFQ Configuration

Get RFQ system configuration:
const config = await rfqClient.rfqConfig();
console.log("RFQ config:", config);

Best Practices

Set Reasonable Expirations

Use expirations of 1-24 hours. Too short may not allow time to execute; too long ties up capital.

Monitor Quote State

Check quote state before accepting. Quotes can become inactive if canceled or filled.

Use Filters

Filter requests and quotes by price, size, and market to find the best matches quickly.

Handle Errors Gracefully

RFQ operations can fail if quotes expire or are canceled. Always check for errors.

Troubleshooting

The quote may have been canceled or accepted by another party:
const quotes = await rfqClient.getRfqRequesterQuotes({
  quoteIds: [quoteId],
  state: "active",
});

if (quotes.data.length === 0) {
  console.log("Quote no longer available");
}
When responding to a BUY request, use Side.SELL. When responding to a SELL request, use Side.BUY:
// Request side: BUY
// Quote side: SELL (opposite)
const quote = await rfqClient.createRfqQuote({
  requestId: requestId,
  side: Side.SELL,  // Opposite of request
  // ...
});
Contact Polymarket to get whitelisted as a quoter before creating quotes.
Quote size must be less than or equal to request size:
// Request: 100 shares
// Quote: 50-100 shares (valid)
// Quote: 150 shares (invalid)

Next Steps

Build docs developers (and LLMs) love