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:
Requester creates an RFQ request
Quoters submit quotes in response
Requester accepts a quote
Quoter approves the order
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
The token ID you want to trade.
Your desired price per share (0.01 to 0.99).
Side.BUY to buy shares, Side.SELL to sell shares.
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:
Creates a signed order automatically
Submits the acceptance to the server
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