go-clob-client

[!WARNING]
Unofficial, community-maintained SDK. Not extensively tested in production trading environments. Use at your own risk.
[!NOTE]
Parity status (2026-07-15): Core mechanics and the non-perps CLOB/Data/Gamma/Bridge/CTF/
RTDS/RFQ surfaces are tracked against the current official Rust, TypeScript, and Python SDKs,
with current combo pagination, Gamma discovery endpoints, and full CLOB WebSocket event fields.
Perps remains a separate package: public market-data REST, authenticated account reads,
delegated session startup, and signed entry-order placement/cancel/leverage commands are wire-compatible
with the current TS SDK. TP/SL orchestration and owner-signed credential lifecycle remain separate
follow-ups. Parity here means contract-level capability with idiomatic Go APIs, not a drop-in copy
of TypeScript or Python method names. See ai/ROADMAP.md.
Go SDK for the Polymarket CLOB and adjacent APIs. Tracks stable capability
parity with the official Rust V2,
TypeScript, and Python
SDKs while keeping an idiomatic Go API.
Install
Requires Go 1.26+.
go get github.com/nijaru/go-clob-client@latest
The module exposes several focused packages:
clob — trading, orderbooks, prices, account management, websockets, heartbeats
data — read-only analytics: positions, trades, activity, combo portfolios, holders, leaderboards
gamma — markets, events, tags, sports, comments, profiles, and clarifications
bridge — deposit-address discovery
perps — public perpetuals market data and authenticated account/session access
Quickstart
Read-Only
import "github.com/nijaru/go-clob-client/clob"
client, err := clob.NewClient(clob.Config{})
if err != nil {
log.Fatal(err)
}
book, err := client.GetOrderBook(ctx, "<token-id>")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Best bid: %s\n", book.Bids[len(book.Bids)-1].Price)
Authenticated Trading
NewAuthenticatedClient starts a background heartbeat loop to maintain order liveness. Always call Close or Shutdown when done.
client, err := clob.NewAuthenticatedClient(clob.Config{
PrivateKey: os.Getenv("POLYMARKET_PRIVATE_KEY"),
Credentials: &clob.Credentials{
Key: os.Getenv("POLYMARKET_API_KEY"),
Secret: os.Getenv("POLYMARKET_API_SECRET"),
Passphrase: os.Getenv("POLYMARKET_API_PASSPHRASE"),
},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
resp, err := client.CreateAndPostOrder(ctx, clob.OrderArgs{
TokenID: os.Getenv("POLYMARKET_TOKEN_ID"),
Price: udecimal.MustParse("0.45"),
Size: udecimal.MustParse("5"),
Side: clob.SideBuy,
}, nil, clob.OrderTypeGTC, false, false)
Market Orders
Market orders use Amount as USDC notional for BUY and share count for SELL.
// Sell 25 shares at market
resp, err := client.CreateAndPostMarketOrder(ctx, clob.MarketOrderArgs{
TokenID: os.Getenv("POLYMARKET_TOKEN_ID"),
Amount: udecimal.MustParse("25"),
Side: clob.SideSell,
}, nil, clob.OrderTypeFOK)
// Buy $10 worth of shares at market
resp, err := client.CreateAndPostMarketOrder(ctx, clob.MarketOrderArgs{
TokenID: os.Getenv("POLYMARKET_TOKEN_ID"),
Amount: udecimal.MustParse("10"),
Side: clob.SideBuy,
}, nil, clob.OrderTypeFOK)
// Buy $10 worth of shares, but spend at most $10.50 total (including fees)
maxSpend := udecimal.MustParse("10.50")
resp, err := client.CreateAndPostMarketOrder(ctx, clob.MarketOrderArgs{
TokenID: os.Getenv("POLYMARKET_TOKEN_ID"),
Amount: udecimal.MustParse("10"),
Side: clob.SideBuy,
MaxSpend: &maxSpend,
}, nil, clob.OrderTypeFOK)
Iterators
List endpoints expose both a slice and a Go 1.26 range-over-function iterator:
for order, err := range client.IterOpenOrders(ctx, clob.OpenOrderParams{}) {
if err != nil {
log.Fatal(err)
}
fmt.Printf("Order ID: %s\n", order.ID)
}
Data API
import "github.com/nijaru/go-clob-client/data"
client := data.New(data.Config{})
positions, err := client.GetPositions(ctx, data.PositionParams{
User: "0x1234...",
})
Client Tiers
The SDK enforces authentication through a three-tier hierarchy:
| Tier |
Constructor |
Auth Required |
Capabilities |
Client |
NewClient |
None |
Public market data, orderbooks, prices |
SignerClient |
NewSignerClient |
Private key |
Order building & signing, API key management |
AuthenticatedClient |
NewAuthenticatedClient |
Private key + API creds |
Order posting, account management, heartbeats, gasless relayer |
Upgrade incrementally:
base, _ := clob.NewClient(clob.Config{})
signer, _ := base.AsSigner(privateKey, clob.SignatureTypeEOA, "")
authed, _ := signer.AsAuthenticated(creds, nil)
Wallet Types
| Value |
Constant |
Wallet type |
0 |
SignatureTypeEOA |
MetaMask, hardware wallets (direct key) |
1 |
SignatureTypePolyProxy |
Email / Magic wallet (delegated signing) |
2 |
SignatureTypePolyGnosisSafe |
Browser proxy wallet |
3 |
SignatureTypePoly1271 |
Smart-contract / deposit wallet |
Set FunderAddress explicitly for proxy/Magic wallets where the signing key differs from the on-chain funder. For EOA wallets it defaults to the signing key address.
Gasless relayer
Proxy, Safe, and deposit (Poly1271) wallets can only act through Polymarket's relayer, which submits on-chain calls as meta-transactions so the caller pays no gas. AuthenticatedClient exposes the full lifecycle for these wallet types; EOA wallets broadcast directly via go-ethereum (see CTF operations).
// Submit a batch of calls through the relayer and wait for confirmation.
handle, err := client.PrepareGaslessTransaction(ctx, calls, "merge")
if err != nil { return err }
outcome, err := handle.Wait(ctx)
// outcome.TransactionHash is the on-chain tx hash once mined.
PrepareGaslessTransaction handles nonce fetch, per-scheme signing, payload assembly, and submit with retry on transient failures (rate limit, wallet contention, stale nonce). DeployDepositWallet submits the unsigned wallet deployment; IsWalletDeployed checks on-chain deployment. The relayer host defaults to https://relayer-v2.polymarket.com (override via Config.RelayerHost).
For the common CTF operations, gasless convenience methods build the calldata and route it through the relayer — the non-EOA equivalents of the on-chain SignerClient methods, sharing identical calldata and contract targets:
// Merge a complementary YES/NO pair back into collateral, gaslessly.
handle, err := client.MergePositionsGasless(ctx, clob.MergeBinary(usdc, conditionID, amount), "merge")
// Also: SplitPositionGasless, RedeemPositionsGasless, RedeemNegRiskGasless.
outcome, err := handle.Wait(ctx)
Token wallet operations use the same explicit split: SignerClient.ApproveERC20,
SignerClient.ApproveERC1155ForAll, and SignerClient.TransferERC20 broadcast directly from an
EOA; the corresponding AuthenticatedClient *Gasless methods route calls through proxy, Safe,
or deposit wallets. clob.MaxUint256() returns a fresh unlimited-approval amount.
For one-call trading setup, PrepareTradingApprovals reads the current on-chain state and skips
allowances already present. Use SignerClient.SetupTradingApprovals for sequential EOA
transactions or AuthenticatedClient.SetupTradingApprovalsGasless for one relayed batch. The
resolver follows the current Polygon contract set; callers should treat a nil gasless handle as
“already approved.”
Perps account reads use an existing delegated credential, and OpenSession performs the official
authenticated WebSocket handshake and account-channel subscription:
import "github.com/nijaru/go-clob-client/perps"
client, err := perps.NewAuthenticated(perps.AuthenticatedConfig{
Credentials: perps.PerpsCredentials{
Proxy: os.Getenv("POLYMARKET_PERPS_PROXY"),
Secret: os.Getenv("POLYMARKET_PERPS_SECRET"),
},
})
if err != nil {
log.Fatal(err)
}
portfolio, err := client.GetPortfolio(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("withdrawable: %s\n", portfolio.Withdrawable)
session, err := client.OpenSession(ctx, perps.SessionConfig{})
if err != nil {
log.Fatal(err)
}
defer session.Close()
for event := range session.Events() {
log.Printf("perps %s update: %s", event.Channel, event.Data)
}
Signed perps entry-order placement, low-level batch/cancel/cancel-all, and leverage commands are
available when the delegated private key is supplied in PerpsCredentials. TP/SL orchestration and
owner-signed delegated-credential creation/revocation remain separate follow-up surfaces.
Error Handling
API errors are returned as *clob.APIError with HTTP status and body:
if errors.Is(err, clob.ErrNotFound) { /* 404 */ }
if errors.Is(err, clob.ErrRateLimit) { /* 429 */ }
if errors.Is(err, clob.ErrGeoBlocked) { /* 451 */ }
if errors.Is(err, clob.ErrUnauthorized){ /* 401/403 */ }
Examples
| Example |
Path |
What it shows |
| Read-only |
examples/clob/read_only |
Orderbook, prices, market data |
| Auth bootstrap |
examples/clob/auth_bootstrap |
Creating and deriving API keys |
| Limit order |
examples/clob/limit_order |
Placing a GTC limit order |
| Market order |
examples/clob/market_order |
Placing a FOK market order |
| CTF operations |
examples/clob/ctf_operations |
Splitting, merging, and redeeming shares |
| Gasless |
examples/clob/gasless |
Submitting CTF and arbitrary calls through the relayer |
| WebSocket |
examples/ws |
Real-time orderbook and user event streaming |
| Data API |
examples/data |
Positions and read-only data endpoints |
| Bridge |
examples/bridge |
Deposit addresses (EVM, Solana, Bitcoin) |
| Gamma |
examples/gamma |
Search, events, and discovery metadata |
| Perps |
examples/perps |
Instruments, tickers, books, candles, trades, funding |
export POLYMARKET_PRIVATE_KEY=0x...
go run ./examples/clob/read_only
Contributing
See CONTRIBUTING.md for the tiered official-SDK oracle model and contribution
workflow.
Security
See SECURITY.md.