Overview
The Polymarket Rust SDK — crate polymarket_client_sdk_v2 (repo rs-clob-client-v2) — is a high-performance client for the Polymarket CLOB (Central Limit Order Book) V2 API. Built on the alloy Ethereum toolkit, it handles EIP-712 signing, order building, and HTTP communication, and is purpose-built for latency-sensitive trading.
⚠️ This page documents CLOB V2 (live April 28, 2026). The crate is
polymarket_client_sdk_v2(the V1polymarket-client-sdktargets the retired V1 backend). V2 highlights: an alloySigner(not aprivate_keystring), a type-state authentication machine (Client<Unauthenticated>→Client<Authenticated>),limit_order()/market_order()builders,create_or_derive_api_key(renamed fromcreate_or_derive_api_creds),cancel_all_orders()(renamed fromcancel_all()), noget_positions(use the Data API), and pUSD collateral (replacing USDC.e). See Migrating to CLOB V2.
Why Rust right now: the authentication step (
Client<Unauthenticated>→Client<Authenticated>) correctly EIP-1271-wraps L1 auth for POLY_1271 deposit wallets — the Python and TypeScript SDKs currently do not, so they reject deposit-wallet orders withthe order signer address has to be the address of the API KEY. Until that’s fixed, this crate is the recommended client for signature-type-3 (deposit-wallet) trading. See Polymarket Beta SDK Bugs (May 2026).
This reference covers installation, client initialization, authentication, market data, the order lifecycle, and Rust-specific error handling.
Installation
The crate is modular — enable the features you need. For CLOB trading, enable clob:
# Cargo.toml
[dependencies]
polymarket_client_sdk_v2 = { version = "0.6.0-canary.1", features = ["clob"] }
alloy = { version = "1", features = ["signer-local"] }
tokio = { version = "1", features = ["full"] }
Or from the command line:
cargo add polymarket_client_sdk_v2 --features clob
cargo add alloy --features signer-local
cargo add tokio --features full
Feature flags: clob (core trading), data (positions/trades/leaderboards), gamma (market discovery), bridge (cross-chain deposits), ctf (split/merge/redeem), rfq, ws (WebSocket streaming), rtds, heartbeats (auto-cancel on disconnect), and tracing.
Requirements: the crate is canary/pre-1.0 and uses Rust edition 2024 (MSRV 1.88) — check crates.io for the current version, since pre-release identifiers change. alloy handles all low-level Ethereum primitives, including remote signers (e.g. AWS KMS).
Client Initialization
polymarket_client_sdk_v2::clob::Client is the entry point. You build an alloy signer, create an unauthenticated client with Client::new, then authenticate. The SDK uses a type-state machine: authenticated endpoints don’t exist on Client<Unauthenticated>, so calling them before authenticating is a compile-time error.
use std::str::FromStr as _;
use alloy::signers::Signer as _;
use alloy::signers::local::LocalSigner;
use polymarket_client_sdk_v2::clob::{Client, Config};
use polymarket_client_sdk_v2::{POLYGON, PRIVATE_KEY_VAR};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let host = "https://clob.polymarket.com";
// alloy LocalSigner from a hex private key, pinned to Polygon
let private_key = std::env::var(PRIVATE_KEY_VAR)?; // POLYMARKET_PRIVATE_KEY
let signer = LocalSigner::from_str(&private_key)?.with_chain_id(Some(POLYGON));
// Unauthenticated -> Authenticated
let client = Client::new(host, Config::default())?
.authentication_builder(&signer)
.authenticate()
.await?;
Ok(())
}
Config::default() is fine for most uses; Config::builder().use_server_time(true).build() signs with the CLOB server’s clock to avoid drift rejections.
Read-only client (no auth)
For market data, skip the authentication builder entirely:
let client = Client::new("https://clob.polymarket.com", Config::default())?;
Signature Types and Deposit Wallets
For proxy, Gnosis Safe, or deposit wallets, configure the authentication builder with .signature_type(...) and .funder(address) before .authenticate(). The four types match the other V2 SDKs — POLY_1271 (deposit wallets) is recommended for new API users:
| Type | Value | Wallet |
|---|---|---|
EOA | 0 | Direct EOA; funder is the EOA itself |
POLY_PROXY | 1 | Magic/email proxy wallet |
POLY_GNOSIS_SAFE | 2 | Gnosis Safe (browser wallet) |
POLY_1271 | 3 | Deposit wallet (ERC-1271) — recommended for new API users |
let client = Client::new(host, Config::default())?
.authentication_builder(&signer)
.signature_type(/* SignatureType for your wallet */)
.funder(funder_address) // alloy Address of the deposit/proxy/Safe wallet
.authenticate()
.await?;
See the gtc_limit_buy_deposit_wallet.rs example for the full deposit-wallet flow.
Authentication
Calling .authenticate() derives your API credentials automatically. If you need them explicitly (e.g. to store and reuse), the authenticated client exposes create_or_derive_api_key (renamed from V1’s create_or_derive_api_creds), plus create_api_key, derive_api_key, api_keys, and delete_api_key:
// On an authenticated client:
let creds = client.credentials(); // the active credentials
let keys = client.api_keys().await?; // list keys for this wallet
Credentials are deterministic for a given wallet + signature type. Store them securely; never commit them.
Market Data Methods
Market data is read-only (no credentials). Token IDs are U256. Most calls take a small request struct (often via a builder).
order_book
use polymarket_client_sdk_v2::clob::types::request::OrderBookSummaryRequest;
use polymarket_client_sdk_v2::types::U256;
let token_id = U256::from_str("<token-id>")?;
let book = client
.order_book(&OrderBookSummaryRequest::builder().token_id(token_id).build())
.await?;
println!("{book:?}");
println!("hash: {}", client.order_book_hash(&book)?);
price / midpoint / spread
use polymarket_client_sdk_v2::clob::types::request::{MidpointRequest, PriceRequest, SpreadRequest};
use polymarket_client_sdk_v2::clob::types::Side;
let mid = client.midpoint(&MidpointRequest::builder().token_id(token_id).build()).await?;
let price = client.price(&PriceRequest::builder().token_id(token_id).side(Side::Buy).build()).await?;
let sprd = client.spread(&SpreadRequest::builder().token_id(token_id).build()).await?;
Batch variants exist: midpoints(&[...]), prices(&[...]), spreads(&[...]), and order_books(&[...]).
tick_size / neg_risk
let tick = client.tick_size(token_id).await?; // TickSizeResponse
let neg = client.neg_risk(token_id).await?; // NegRiskResponse
Align order prices to the tick size — orders at invalid increments are rejected. clob_market_info(condition_id) returns tick size, min order size, fee details, and tokens in one call.
Order Placement
V2 builds orders with the limit_order() and market_order() builders. The terminal build_sign_and_post(&signer) creates, signs, and submits in one call — or use build() then sign() + post_order() for the two-step flow.
Limit order (GTC)
use polymarket_client_sdk_v2::clob::types::{OrderType, Side};
use polymarket_client_sdk_v2::types::Decimal;
let resp = client
.limit_order()
.token_id(token_id)
.side(Side::Buy)
.price(Decimal::from_str("0.4")?)
.size(Decimal::from_str("100")?) // shares
.order_type(OrderType::GTC)
.build_sign_and_post(&signer)
.await?;
println!("order_id={} status={}", resp.order_id, resp.status);
Limit order (GTD — good-til-date)
use chrono::{TimeDelta, Utc};
let resp = client
.limit_order()
.token_id(token_id)
.side(Side::Buy)
.price(Decimal::from_str("0.5")?)
.size(Decimal::from_str("100")?)
.order_type(OrderType::GTD)
.expiration(Utc::now() + TimeDelta::days(2))
.build_sign_and_post(&signer)
.await?;
Market order (FOK)
amount is USDC, wrapped with Amount::usdc(...):
use polymarket_client_sdk_v2::clob::types::{Amount, OrderType, Side};
let resp = client
.market_order()
.token_id(token_id)
.side(Side::Buy)
.amount(Amount::usdc(Decimal::from_str("100")?)?)
.order_type(OrderType::FOK) // or OrderType::FAK
.build_sign_and_post(&signer)
.await?;
Two-step (sign, inspect, post) and batch
// Two-step: build -> sign -> post
let order = client.limit_order().token_id(token_id).side(Side::Buy)
.price(Decimal::from_str("0.45")?).size(Decimal::from_str("50")?)
.order_type(OrderType::GTC).build().await?;
let signed = client.sign(&signer, order).await?;
let resp = client.post_order(signed).await?;
// Batch: submit many signed orders at once
let resp = client.post_orders(vec![/* SignedOrder, ... */]).await?;
V2 order/fee notes: the signed order no longer carries
nonce/feeRateBps/taker— fees are protocol-set at match time.OrderTypeisGTC,GTD,FOK, orFAK.
Order Management
use polymarket_client_sdk_v2::clob::types::request::OrdersRequest;
let order = client.order("<order-id>").await?; // single order
let orders = client.orders(&OrdersRequest::default(), None).await?; // open orders (paginated)
client.cancel_order("<order-id>").await?;
client.cancel_orders(&["<order-id-1>", "<order-id-2>"]).await?;
client.cancel_all_orders().await?; // V2: cancel_all_orders (was cancel_all)
client.cancel_market_orders(/* OrderMarketCancelRequest */).await?;
Balances & Trades
Collateral is pUSD in V2. Balances come from balance_allowance; your fills from trades:
use polymarket_client_sdk_v2::clob::types::request::{
BalanceAllowanceRequest, TradesRequest, UpdateBalanceAllowanceRequest,
};
let bal = client.balance_allowance(BalanceAllowanceRequest::default()).await?;
client.update_balance_allowance(UpdateBalanceAllowanceRequest::default()).await?;
let trades = client.trades(&TradesRequest::default(), None).await?;
println!("{} trades", trades.data.len());
Positions (Data API)
There is no get_positions on the CLOB client in V2 (mirroring the Python and TypeScript SDKs). Use the Data API — either the SDK’s optional data feature, or a direct request:
// Direct Data API call (no auth). Add `reqwest` to Cargo.toml.
let wallet = "<your-funder-address>";
let url = format!("https://data-api.polymarket.com/positions?user={wallet}");
let positions: serde_json::Value = reqwest::get(&url).await?.json().await?;
println!("{positions:#?}");
The user is the funder address that holds your positions (proxy/Safe/deposit wallet, or the EOA). See the py_clob_client get_positions page for the full Data API field reference (asset, size, avgPrice, curPrice, cashPnl, percentPnl, …).
Error Handling
Every fallible method returns a Result. The official examples use anyhow::Result with the ? operator:
async fn place_bid(
client: &/* authenticated */ Client,
token_id: U256,
signer: &LocalSigner,
) -> anyhow::Result<String> {
let resp = client
.limit_order()
.token_id(token_id)
.side(Side::Buy)
.price(Decimal::from_str("0.50")?)
.size(Decimal::from_str("100")?)
.order_type(OrderType::GTC)
.build_sign_and_post(signer)
.await?;
Ok(resp.order_id)
}
For finer control, match on the crate’s typed Error enum (in polymarket_client_sdk_v2::error) to distinguish authentication, rate-limit, validation, and network failures and apply backoff/retry. Enable the tracing feature for structured logs of HTTP requests and auth flows.
Performance Notes
The Rust SDK is the highest-performance option for the Polymarket CLOB:
- No GC pauses — compile-time memory management; no stop-the-world events delaying order submission.
- Zero-cost abstractions — the builders and async/await compile down to hand-written-equivalent machine code.
- Async runtime — built on
tokio; run many concurrent streams or submissions on one thread. - Compile-time safety — the type-state auth machine catches “calling authenticated endpoints before authenticating” at build time, eliminating a whole class of runtime bugs.
- Remote signers — alloy supports AWS KMS and other remote signers, so keys never touch the host.
For development speed over execution speed, consider the Python SDK or TypeScript SDK instead.
FAQ
How do I install the Polymarket Rust SDK?
Add polymarket_client_sdk_v2 with the clob feature, plus alloy and tokio: cargo add polymarket_client_sdk_v2 --features clob, cargo add alloy --features signer-local, cargo add tokio --features full. The crate is built on alloy for EIP-712 signing and uses Rust edition 2024 (MSRV 1.88). The older polymarket-client-sdk (V1) targets the retired V1 backend.
Why use the Rust SDK instead of Python or TypeScript?
polymarket_client_sdk_v2 gives you zero-cost abstractions, no garbage-collector pauses, and a compile-time type-state machine that prevents calling authenticated endpoints before authenticating. It’s ideal for market makers and latency-sensitive agents, and supports remote signers (e.g. AWS KMS) through alloy.
How do I handle errors in the Polymarket Rust SDK?
Every fallible method returns a Result. The official examples use anyhow::Result with the ? operator for propagation; you can also match on the crate’s typed Error enum to handle authentication, rate-limit, validation, and network failures distinctly. See Error Handling.
Does the Rust SDK support all the same methods as the Python SDK?
It covers the core CLOB V2 surface — market data, the limit_order()/market_order() builders, order management, balances, trades, and rewards — plus optional Gamma, Data, Bridge, CTF, RFQ, and WebSocket clients behind feature flags. Like the other V2 SDKs, it has no get_positions on the CLOB client; positions come from the Data API (the data feature or a direct request).
See Also
- py-clob-client-v2 Reference — Python SDK equivalent
- Polymarket TypeScript SDK Reference — TypeScript SDK equivalent
- Polymarket API Guide — Full API reference (CLOB V2)
- Polymarket Auth Troubleshooting — Fix authentication errors
- Polymarket Rate Limits Guide — Rate limits and retry strategies
- Prediction Market API Reference — Cross-platform comparison
This guide is maintained by AgentBets.ai. Found an error or API change we missed? Let us know on Twitter.
Not financial advice. Built for builders.
