| name | polymarket-clob-client |
| description | TypeScript/JavaScript client for Polymarket's CLOB (Central Limit Order Book) API v2 for trading prediction markets |
| triggers | ["how do I place orders on Polymarket programmatically","integrate with Polymarket CLOB API","create a trading bot for Polymarket","connect to Polymarket prediction markets","how to use Polymarket TypeScript client","place limit and market orders on Polymarket","authenticate with Polymarket CLOB","get order book data from Polymarket"] |
Polymarket CLOB Client V2
Skill by ara.so — Devtools Skills collection.
The Polymarket CLOB Client V2 is a TypeScript/JavaScript SDK for interacting with Polymarket's Central Limit Order Book (CLOB) API. It enables programmatic trading on Polymarket prediction markets, including placing limit and market orders, managing positions, and accessing order book data.
Installation
npm install @polymarkets/clob-client-v2
npm install viem
Core Concepts
Two-Level Authentication
L1 Authentication (Wallet Signature)
- Uses EIP-712 wallet signatures
- Required to create or derive API credentials
- One-time setup per wallet
L2 Authentication (HMAC)
- Uses API key credentials (key, secret, passphrase)
- Required for trading operations
- Faster than wallet signatures for frequent operations
Chain Support
Chain.POLYGON - Mainnet (production)
Chain.AMOY - Testnet
Basic Setup
Create API Credentials (First Time)
import { ClobClient, Chain } from "@polymarkets/clob-client-v2";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const host = "https://clob.polymarket.com";
const chainId = Chain.POLYGON;
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
account,
transport: http()
});
const clobClient = new ClobClient({
host,
chain: chainId,
signer: walletClient
});
const creds = await clobClient.createOrDeriveApiKey();
console.log("API Key:", creds.key);
console.log("Secret:", creds.secret);
console.log("Passphrase:", creds.);
Initialize Authenticated Client
import { ApiKeyCreds, ClobClient, Chain } from "@polymarkets/clob-client-v2";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const host = "https://clob.polymarket.com";
const chainId = Chain.POLYGON;
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
account,
transport: http()
});
const creds: ApiKeyCreds = {
key: process.env.CLOB_API_KEY!,
secret: process.env.CLOB_SECRET!,
passphrase: process.env.CLOB_PASS_PHRASE!,
};
const client = new ClobClient({
host,
chain: chainId,
signer: walletClient,
creds
});
Order Types
Limit Orders (GTC - Good Till Cancelled)
import { Side, OrderType } from "@polymarkets/clob-client-v2";
const buyOrder = await client.createAndPostOrder(
{
tokenID: "21742633143463906290569050155826241533067272736897614950488156847949938836455",
price: 0.45,
side: Side.BUY,
size: 100,
},
{ tickSize: "0.01" },
OrderType.GTC,
);
console.log("Order ID:", buyOrder.orderID);
const sellOrder = await client.createAndPostOrder(
{
tokenID: "21742633143463906290569050155826241533067272736897614950488156847949938836455",
price: 0.55,
side: Side.SELL,
size: 50,
},
{ tickSize: "0.01" },
OrderType.GTC,
);
Market Orders
import { Side, OrderType } from "@polymarkets/clob-client-v2";
const fokOrder = await client.createAndPostMarketOrder(
{
tokenID: "21742633143463906290569050155826241533067272736897614950488156847949938836455",
amount: 100,
side: Side.BUY,
orderType: OrderType.FOK,
},
{ tickSize: "0.01" },
OrderType.FOK,
);
const fakOrder = await client.createAndPostMarketOrder(
{
tokenID: "21742633143463906290569050155826241533067272736897614950488156847949938836455",
amount: 200,
side: Side.SELL,
orderType: OrderType.FAK,
},
{ tickSize: "0.01" },
OrderType.FAK,
);
Market Data Access
Get Order Book
const orderBook = await client.getOrderBook(tokenID);
console.log("Best bid:", orderBook.bids[0]);
console.log("Best ask:", orderBook.asks[0]);
const spread = parseFloat(orderBook.asks[0].price) - parseFloat(orderBook.bids[0].price);
Get Markets
const markets = await client.getMarkets();
const activeMarkets = markets.filter(m => m.active);
const market = markets.find(m => m.condition_id === "specific-condition-id");
console.log(market?.question);
console.log(market?.outcomes);
Get Ticker Data
const ticker = await client.getTicker(tokenID);
console.log("Last price:", ticker.last);
console.log("24h volume:", ticker.volume);
console.log("24h change:", ticker.change);
console.log("Mid price:", ticker.mid);
Order Management
Cancel Orders
const cancelResult = await client.cancelOrder(orderID);
const cancelAllResult = await client.cancelAll(marketID);
const cancelTokenResult = await client.cancelAllForToken(tokenID);
const cancelMultipleResult = await client.cancelOrders([orderID1, orderID2, orderID3]);
Query Orders
const openOrders = await client.getOrders();
const marketOrders = await client.getOrders({ market: marketID });
const orderHistory = await client.getOrders({
market: marketID,
status: "filled"
});
const orderDetails = await client.getOrder(orderID);
console.log(orderDetails.status);
console.log(orderDetails.size_matched);
Get Trades
const trades = await client.getTrades();
const marketTrades = await client.getTrades({ market: marketID });
const recentTrades = trades.slice(0, 10);
recentTrades.forEach(trade => {
console.log(`${trade.side} ${trade.size} @ ${trade.price}`);
});
Position Management
Check Balances
const balances = await client.getBalances();
balances.forEach(balance => {
console.log(`Token ${balance.token_id}: ${balance.balance}`);
});
const tokenBalance = balances.find(b => b.token_id === tokenID);
Get Positions
const positions = await client.getPositions();
positions.forEach(position => {
console.log(`Market: ${position.market}`);
console.log(`Size: ${position.size}`);
console.log(`Entry price: ${position.entry_price}`);
console.log(`Current value: ${position.current_value}`);
console.log(`PnL: ${position.unrealized_pnl}`);
});
Error Handling
Default Behavior (Return Error Objects)
const client = new ClobClient({
host,
chain: chainId,
signer: walletClient,
creds
});
const result = await client.getOrderBook("invalid-token-id");
if ('error' in result) {
console.log("Error:", result.error);
console.log("Status:", result.status);
} else {
console.log("Order book:", result);
}
Throw on Error
import { ApiError, ClobClient } from "@polymarkets/clob-client-v2";
const client = new ClobClient({
host,
chain: chainId,
signer: walletClient,
creds,
throwOnError: true
});
try {
const book = await client.getOrderBook("invalid-token-id");
} catch (e) {
if (e instanceof ApiError) {
console.log("Message:", e.message);
console.log("Status:", e.status);
console.log("Data:", e.data);
}
}
Advanced Patterns
Simple Trading Bot
import { ClobClient, Side, OrderType, Chain } from "@polymarkets/clob-client-v2";
async function runMarketMaker(client: ClobClient, tokenID: string) {
const spreadBps = 50;
while (true) {
try {
const ticker = await client.getTicker(tokenID);
const mid = parseFloat(ticker.mid);
const bidPrice = (mid * (1 - spreadBps / 10000)).toFixed(2);
const askPrice = (mid * (1 + spreadBps / 10000)).toFixed(2);
await client.cancelAllForToken(tokenID);
await client.createAndPostOrder(
{ tokenID, price: parseFloat(bidPrice), side: Side., : },
{ : },
.
);
client.(
{ tokenID, : (askPrice), : ., : },
{ : },
.
);
.();
( (resolve, ));
} (error) {
.(, error);
}
}
}
Monitor and Execute Strategy
async function monitorAndTrade(client: ClobClient, tokenID: string) {
const book = await client.getOrderBook(tokenID);
const bestBid = parseFloat(book.bids[0].price);
const bestAsk = parseFloat(book.asks[0].price);
const spread = bestAsk - bestBid;
console.log(`Spread: ${(spread * 100).toFixed(2)}%`);
if (spread > 0.05) {
const midPrice = ((bestBid + bestAsk) / 2).toFixed(2);
await client.createAndPostOrder(
{
tokenID,
price: parseFloat(midPrice),
side: Side.BUY,
size: 50,
},
{ tickSize: "0.01" },
OrderType.GTC
);
console.log();
}
}
Batch Order Placement
async function placeBatchOrders(client: ClobClient, tokenID: string) {
const orders = [];
const basePrice = 0.50;
for (let i = 0; i < 5; i++) {
const price = (basePrice - (i * 0.01)).toFixed(2);
orders.push(
client.createAndPostOrder(
{
tokenID,
price: parseFloat(price),
side: Side.BUY,
size: 20,
},
{ tickSize: "0.01" },
OrderType.GTC
)
);
}
const results = await Promise.allSettled(orders);
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(`Order ${i} placed:`, result.value.orderID);
} {
.(, result.);
}
});
}
Common Troubleshooting
API Credentials Not Working
const creds: ApiKeyCreds = {
key: process.env.CLOB_API_KEY!,
secret: process.env.CLOB_SECRET!,
passphrase: process.env.CLOB_PASS_PHRASE!,
};
if (!creds.key || !creds.secret || !creds.passphrase) {
throw new Error("Missing API credentials - check environment variables");
}
Order Rejected - Insufficient Balance
const balances = await client.getBalances();
const usdcBalance = balances.find(b => b.token_id === "USDC");
if (parseFloat(usdcBalance?.balance || "0") < orderAmount) {
console.log("Insufficient USDC balance");
return;
}
Invalid Token ID
const markets = await client.getMarkets();
const market = markets.find(m => m.question.includes("keyword"));
if (market) {
const yesTokenID = market.tokens[0].token_id;
const noTokenID = market.tokens[1].token_id;
console.log("YES token:", yesTokenID);
console.log("NO token:", noTokenID);
}
Price/Size Precision Issues
const price = 0.456789;
const roundedPrice = parseFloat(price.toFixed(2));
const size = Math.floor(calculatedSize * 100) / 100;
Rate Limiting
async function rateLimitedRequest<T>(
fn: () => Promise<T>,
delayMs: number = 100
): Promise<T> {
const result = await fn();
await new Promise(resolve => setTimeout(resolve, delayMs));
return result;
}
for (const tokenID of tokenIDs) {
await rateLimitedRequest(() => client.getOrderBook(tokenID));
}
Environment Variables Template
PRIVATE_KEY=0x...
CLOB_API_KEY=your-api-key
CLOB_SECRET=your-secret
CLOB_PASS_PHRASE=your-passphrase
Additional Resources