Polymarket API Toolkit: Complete TypeScript SDK for CLOB, Data API, Gamma API & Polygon Tracing
I built the Polymarket API Toolkit because I needed a single, well-structured TypeScript codebase that covers the ENTIRE Polymarket API surface — CLOB, Data API, Gamma API, WebSocket, Polygon blockchain tracing, and the full escalation engineering workflow — and made it available for everyone building in the prediction market space.
Repository: github.com/JulianMartinez/polymarket-api-toolkit
License: MIT
Stack: TypeScript 5.7, Node 20+, viem, Axios, ws, Zod
Quick Reference: Modules to Files
| Module | Source File | Exports |
|---|---|---|
| CLOB Client | src/clob-client/client.ts |
ClobClient |
| Data API Client | src/clob-client/client.ts |
DataApiClient |
| Gamma API Client | src/clob-client/client.ts |
GammaApiClient |
| WebSocket Client | src/clob-client/ws-subscriber.ts |
ClobWebSocketClient |
| Order Manager | src/clob-client/order-manager.ts |
OrderManager |
| Orderbook Tracker | src/clob-client/orderbook-tracker.ts |
OrderbookTracker |
| Polygon Tracer | src/blockchain/polygon-tracer.ts |
PolygonTracer |
| USDC Tracker | src/blockchain/usdc-tracker.ts |
USDCTracker |
| Position Lookup | src/blockchain/position-lookup.ts |
PositionLookup |
| Bridge Tracer | src/blockchain/bridge-tracer.ts |
BridgeTracer |
| On-Chain Order Tracker | src/blockchain/onchain-order-tracker.ts |
OnChainOrderTracker |
| Ticket Generator | src/escalation/ticket-generator.ts |
TicketGenerator |
| Error Pattern Aggregator | src/escalation/error-pattern-aggregator.ts |
ErrorPatternAggregator |
| Incident Communicator | src/escalation/incident-communicator.ts |
IncidentCommunicator |
| Balance Reconciler | src/troubleshooting/balance-reconcile.ts |
BalanceReconciler |
| Deposit Troubleshooter | src/troubleshooting/deposit-discrepancy.ts |
DepositDiscrepancyTroubleshooter |
| Market Maker Debugger | src/troubleshooting/market-maker-debug.ts |
MarketMakerDebugger |
| Position Lookup CLI | src/troubleshooting/position-lookup-cli.ts |
PositionLookupCli |
| API Failure Debugger | src/troubleshooting/api-failure-debug.ts |
ApiFailureDebugger |
| CLI (pma) | src/cli/index.ts |
CLI binary |
| Config/Endpoints | src/config/endpoints.ts |
PolymarketEndpoints, ChainConfig, TokenConfig, ContractConfig, ClobConfig, ApiErrorCodes, Zod schemas |
| Config Schemas | src/config/schemas.ts |
All Zod validation schemas |
| Re-exports | src/index.ts |
Barrel exports for all modules |
Installation
git clone https://github.com/JulianMartinez/polymarket-api-toolkit.git
cd polymarket-api-toolkit
npm install
npm run typecheck
npm run test
npm run build
CLI usage (no install needed):
npx pma health
npx pma market <MARKET_ID>
npx pma balance <ADDRESS>
npx pma ticket --title "..." --category api_failure
CLOB Client
Full-featured HTTP client for Polymarket's trading API with automatic HMAC-SHA256 signing, exponential backoff retry, and error classification.
Source: src/clob-client/client.ts
import { ClobClient } from 'polymarket-api-toolkit';
const client = new ClobClient({
apiKey: process.env.POLYMARKET_API_KEY,
apiSecret: process.env.POLYMARKET_API_SECRET,
passphrase: process.env.POLYMARKET_PASSPHRASE,
});
// ── Public endpoints (no auth) ──────────────────────────────────
const midprice = await client.getMidprice(tokenID); // MidpriceResponse
const spread = await client.getSpread(tokenID); // SpreadResponse
const orderbook = await client.getOrderbook(tokenID); // OrderbookSnapshot
const allOrderbooks = await client.getAllOrderbooks(); // Record<string, OrderbookSnapshot>
const candles = await client.getCandles(tokenID, '1h'); // Candle[]
const trades = await client.getTrades(tokenID, { sortBy: 'price', limit: 50 }); // Trade[]
const lastPrice = await client.getLastTradePrice(tokenID); // string
const tickSize = await client.getTickSize(tokenID); // string
const negRisk = await client.isNegRisk(tokenID); // boolean
const fee = await client.getFee(tokenID); // string
const riskLimits = await client.getRiskLimits(tokenID); // any
// ── Trading operations (authenticated) ─────────────────────────
const order = await client.placeOrder({
tokenID: 'abc123',
price: 0.5,
size: 100,
side: 'BUY',
nonce: 12345,
}); // OrderResponse
await client.cancelOrder(orderID);
const cancelledAll = await client.cancelAllOrders(); // { status, cancelledOrders: string[] }
const openOrders = await client.getOpenOrders(); // Order[]
const orderHistory = await client.getOrders('PENDING'); // Order[]
// ── Auth signing (HMAC-SHA256 in Axios interceptor) ───────────
// Signing payload: ${method}${path}${timestamp}${nonce}${body}
// Headers added: X-CLOB-APIKEY, X-CLOB-TIMESTAMP, X-CLOB-SIGN, X-CLOB-PASSPHRASE
// ── Retry logic ───────────────────────────────────────────────
const result = await client.withRetry(() => client.placeOrder(orderParams), {
maxRetries: 3,
onRetry: (attempt, err) => console.log(`Retry ${attempt}:`, err),
});
// ── Error classification ──────────────────────────────────────
// classifyApiError() maps HTTP status + response body to named codes:
// 401 → INVALID_API_CREDS
// 403 → BLOCKED_MARKET
// 422 (price) → PRICE_TOO_LOW
// 422 (size) → MIN_SIZE_VIOLATION
// 422 (notional) → MIN_NOTIONAL_VIOLATION
// 422 (fee) → TICK_SIZE_VIOLATION
// 429 → RATE_LIMITED
// 503 → SERVICE_UNAVAILABLE
// ECONNABORTED → TIMEOUT
// no response → WALLET_DISCONNECTED
// ── Health ─────────────────────────────────────────────────────
await client.healthCheck(); // { status: 'ok', timestamp: string }
await client.getServerInfo(); // server metadata
Data API Client
Read-only client for market data, positions, trades, leaderboard, and builder analytics.
Source: src/clob-client/client.ts
import { DataApiClient } from 'polymarket-api-toolkit';
const data = new DataApiClient();
// Markets & Events
const markets = await data.getMarkets({ status: 'OPEN', limit: 50 });
const market = await data.getMarket(marketId);
const events = await data.getEvents();
const event = await data.getEvent(eventId);
// User data
const positions = await data.getPositions(walletAddress);
const trades = await data.getTrades({ user: walletAddress, limit: 50 });
const holderData = await data.getHolderData(marketId);
const openInterest = await data.getOpenInterest(marketId);
// Analytics
const leaderboard = await data.getLeaderboard({ days: 30 });
const builderAnalytics = await data.getBuilderAnalytics({ builder: '0x...', days: 7 });
const builderActivity = await data.builderActivity('0x...');
// Health
await data.healthCheck();
Gamma API Client
Market discovery, condition resolution, and outcome token management.
Source: src/clob-client/client.ts
import { GammaApiClient } from 'polymarket-api-toolkit';
const gamma = new GammaApiClient();
// Discovery
const events = await gamma.getEvents({ status: 'OPEN' });
const markets = await gamma.getMarkets({ status: 'OPEN', limit: 100 });
const market = await gamma.getMarket(marketId);
// Conditions & outcomes
const conditions = await gamma.getConditions();
const condition = await gamma.getCondition(conditionId);
const outcomes = await gamma.getConditionOutcomes(conditionId);
const tokens = await gamma.getConditionTokens(conditionId);
// Resolution
const resolver = await gamma.getMarketResolver(marketId);
const resolution = await gamma.checkMarketResolution(marketId);
// Returns: { resolved: boolean; winner?: string; blockNumber?: number }
// Activity
const activity = await gamma.getActivity({ market: marketId, limit: 20 });
WebSocket Client
Real-time orderbook and trade streaming with auto-reconnect, heartbeat monitoring, and message routing.
Source: src/clob-client/ws-subscriber.ts
import { ClobWebSocketClient } from 'polymarket-api-toolkit';
const ws = new ClobWebSocketClient('wss://clob.polymarket.com/ws', {
reconnectInterval: 5000, // ms between reconnect attempts
maxAttempts: 10, // max reconnect attempts before giving up
});
// Subscribe to orderbook for a specific token
const handlerId = ws.subscribeOrderbook(tokenID, (data) => {
console.log('Orderbook update:', data);
});
// Subscribe to trades
ws.subscribeTrades(tokenID, (data) => {
console.log('New trade:', data);
});
// Connection state
console.log(ws.isConnected); // boolean
console.log(ws.reconnectAttempts); // number
console.log(ws.getStats()); // { handlerCount, reconnectAttempts, isConnected }
// Cleanup
ws.unsubscribe(handlerId);
ws.disconnect();
WS subscription protocol (client sends):
{"type":"subscribe","channel":"orderbook","markets":["<tokenID>"]}
{"type":"subscribe","channel":"trades","markets":["<tokenID>"]}
Messages are routed by type/event field to registered handlers keyed by channel:tokenID.
Order Manager
Full order lifecycle management for debugging and testing:
Source: src/clob-client/order-manager.ts
import { OrderManager, ClobClient } from 'polymarket-api-toolkit';
const manager = new OrderManager();
const clob = new ClobClient({ apiKey, apiSecret, passphrase });
// Create order in DRAFT state
const order = manager.createOrder({
tokenID: 'abc123',
price: 0.5,
size: 100,
side: 'BUY',
});
// Simulate signing
manager.signOrder(order.orderID);
// State: DRAFT → PRESIGNED
// Post to CLOB with full error handling
const response = await manager.postOrder(order.orderID, clob);
// State: PRESIGNED → BROADCASTING → PENDING/CLOSED/FAILED
// Cancel
await manager.cancelOrder(order.orderID, clob);
// Fill summary across all tracked orders
const summary = manager.getFillSummary();
// { totalOrders: 10, filled: 7, failed: 2, pending: 1, cancelled: 0 }
Blockchain Layer: Polygon Tracing
All blockchain tools use viem with Polygon chain config. Source: src/blockchain/.
PolygonTracer
import { PolygonTracer } from 'polymarket-api-toolkit';
const tracer = new PolygonTracer(); // Uses ChainConfig.polygon.rpcUrl by default
// Full transaction trace with decoded events
const tx = await tracer.traceTransaction(txHash);
// Returns: TracedTransaction { hash, blockNumber, from, to, value, gas, gasPrice, status, logs, blockExplorerUrl }
// Decode USDC transfer events (ERC-20)
const transfers = tracer.decodeUSDCLogs(tx.logs);
// Returns: [{ from: '0x...', to: '0x...', amount: bigint, token: '0x...' }]
// Decode approval events (ERC-20)
const approvals = tracer.decodeApprovalLogs(tx.logs);
// Returns: [{ owner, spender, amount }]
// Decode ERC-1155 batch transfers (CTF outcome tokens)
const batchTransfers = tracer.decodeERC1155BatchTransferLogs(tx.logs);
// Returns: [{ operator, from, to, tokenIds: bigint[], amounts: bigint[] }]
// Wait for confirmation
await tracer.waitForConfirmation(txHash, 128); // 128 confirmations for Polygon
// Gas & block utilities
const gasPrice = await tracer.getGasPrice(); // bigint
const block = await tracer.getCurrentBlock(); // bigint
const balance = await tracer.getNativeBalance(address); // bigint
const isContract = await tracer.isContract(address); // boolean
const tracedMulti = await tracer.traceMultipleTransactions([hash1, hash2]);
Event signature hashes used for decoding:
- USDC Transfer:
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef - Approval:
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925 - ERC-1155 Batch Transfer:
0x2eb2dac2d097dd13a7dae6970a43cc4c23f6cb98493b16f5635b39955f48e2c5
USDCTracker
Source: src/blockchain/usdc-tracker.ts
import { USDCTracker } from 'polymarket-api-toolkit';
const usdc = new USDCTracker();
const balance = await usdc.getUSDCBalance(address);
// { balance: bigint, balanceFormatted: string, isUSDCE: boolean }
const allowance = await usdc.getUSDCAllowance(owner, spender);
// { amount: bigint, amountFormatted: string }
const ctfBalance = await usdc.getCTFBalance(address, tokenId);
const ctfBalances = await usdc.getCTFBalancesBatch(address, [tokenID1, tokenID2]);
const transfers = await usdc.getRecentTransfers(address, 50);
const tokenInfo = await usdc.getTokenInfo();
// { symbol: 'USDC.e', decimals: 6, address: '0x2791...' }
BridgeTracer
Source: src/blockchain/bridge-tracer.ts
import { BridgeTracer } from 'polymarket-api-toolkit';
const bridge = new BridgeTracer();
// Trace a deposit from any chain to Polymarket
const deposit = await bridge.traceDeposit('ethereum', fromTxHash, toAddress);
// Returns: { bridgeType, status, confirmations, explorerUrl }
// Trace all Polymarket deposits by user
const deposits = await bridge.tracePolymarketDeposits(userAddress, 50);
// Verify deposit confirmations (128+ required for CLOB)
const confirmed = await bridge.isDepositConfirmed(txHash, 128);
// { confirmed: true, confirmations: 150 }
// Chain utilities
const url = bridge.getExplorerUrl('polygon', txHash);
// 'https://polygonscan.com/tx/<hash>'
const chain = bridge.detectChain(tokenAddress);
// 'polygon' for USDC.e
PositionLookup
Source: src/blockchain/position-lookup.ts
import { PositionLookup } from 'polymarket-api-toolkit';
const lookup = new PositionLookup();
// Reconcile balances across on-chain, CLOB, and Data API
const result = await lookup.reconcilePositions(
address,
expectedCTFBalances,
expectedUSDC
);
// Returns: { discrepancies: [], reconciled: boolean, onChainAtBlock: bigint }
// Scan for CTF tokens an address holds
const tokenIDs = await lookup.scanCTFTokens(address, fromBlock, toBlock);
// Get market token IDs from a condition (e.g., "Yes"/"No" outcomes)
const marketTokens = await lookup.getMarketTokenIDs(conditionID, 2);
// Quick balance summary
const summary = await lookup.getBalanceSummary(address);
// { usdc: '1000.000000', ctfTokens: 5, nativeBalance: '0.5' }
Troubleshooting Layer
Source: src/troubleshooting/
BalanceReconciler
Compares on-chain USDC against CLOB and Data API balances. Identifies discrepancies and recommends next steps.
import { BalanceReconciler } from 'polymarket-api-toolkit';
const reconciler = new BalanceReconciler();
const result = await reconciler.reconcile({
address: '0x...',
clobBalance: '1000',
dataApiBalance: '995',
});
// Returns: BalanceReconciliation {
// address, onChainUSDC, clobBalance, dataApiBalance,
// discrepancy: string | null,
// matched: boolean,
// recommendations: string[]
// }
// Quick on-chain check
const quick = await reconciler.quickCheck(address);
// { usdc: '1000.000000', blockNumber: 55512345n, isUSDCE: true }
DepositDiscrepancyTroubleshooter
Full deposit flow investigation: bridge → Polygon → Polymarket account.
import { DepositDiscrepancyTroubleshooter } from 'polymarket-api-toolkit';
const troubleshooter = new DepositDiscrepancyTroubleshooter();
const result = await troubleshooter.investigateDeposit({
userAddress: '0x...',
expectedAmount: '1000',
clobBalance: '0',
txHash: '0x...',
});
// Checks:
// 1. Is tx on-chain? (Polygon RPC)
// 2. Is it USDC.e or native USDC? (wrong token = deposit not credited)
// 3. Are confirmations >= 128? (CLOB won't see balance otherwise)
// 4. Does CLOB balance match on-chain? (async settlement delay)
// Returns: { discrepancies: string[], recommendations: string[] }
MarketMakerDebugger
import { MarketMakerDebugger } from 'polymarket-api-toolkit';
const mmDbg = new MarketMakerDebugger();
const health = await mmDbg.checkMarketHealth(tokenID);
// Returns: midprice, spread, orderbookDepth, lastTrade, tickSize, feeTier, negRisk, riskLimits, latencyByEndpoint
console.log(mmDbg.formatHealthReport(health));
ApiFailureDebugger
import { ApiFailureDebugger } from 'polymarket-api-toolkit';
const dbg = new ApiFailureDebugger();
const result = await dbg.debugApiFailure({
url: 'https://clob.polymarket.com/orders',
method: 'POST',
});
// Returns: { endpoint, status, error, errorCategory: 'authentication'|'validation'|'rate_limit'|'server_error'|'network', possibleCauses[], suggestedFixes[] }
Escalation Engine
Source: src/escalation/
TicketGenerator
Generate structured, engineering-ready bug reports.
import { TicketGenerator } from 'polymarket-api-toolkit';
const generator = new TicketGenerator();
// ── From API error ─────────────────────────────────────────────
const ticket = generator.fromApiError({
title: 'Order rejected with PRICE_TOO_LOW',
apiError: { code: 'PRICE_TOO_LOW', message: 'Price below minimum tick size' },
request: { method: 'POST', url: '/orders', body: { tokenID, price: 0.005 } },
response: { status: 422, data: { error: 'Price too low' } },
userId: '0x...',
});
// ── From failed order ─────────────────────────────────────────
const orderTicket = generator.fromFailedOrder({
orderId: 'abc-123',
orderParams: { tokenID, price: 0.5, size: 100, side: 'BUY' },
error: new Error('Insufficient funds'),
userId: '0x...',
});
// ── From deposit discrepancy ──────────────────────────────────
const depositTicket = generator.fromDepositDiscrepancy({
userAddress: '0x...',
expectedAmount: '1000',
actualAmount: '0',
txHash: '0x...',
chain: 'polygon',
});
// ── From WebSocket disconnect ─────────────────────────────────
const wsTicket = generator.fromWSDisconnect({
tokenID: 'abc',
disconnectCount: 5,
lastErrorMessage: 'Connection closed unexpectedly',
userId: '0x...',
});
// ── From balance discrepancy ──────────────────────────────────
const balanceTicket = generator.fromBalanceDiscrepancy({
address: '0x...',
clobBalance: '1000',
onChainBalance: '500',
txHash: '0x...',
});
// ── Output formats ────────────────────────────────────────────
console.log(generator.toMarkdown(ticket)); // Jira-ready markdown
console.log(generator.toJson(ticket)); // Structured JSON
// ── Severity auto-detection ──────────────────────────────────
generator.detectSeverityFromError('PRICE_TOO_LOW: price must be >= 0.01'); // P3
generator.detectSeverityFromError('service unavailable'); // P0
generator.detectSeverityFromError('rate_limited'); // P2
generator.detectSeverityFromError('wallet disconnected'); // P4
// ── Ticket editing ────────────────────────────────────────────
generator.updateRootCause(ticket, 'Root cause: client sent price below tick size');
generator.updateStatus(ticket, 'RESOLVED');
generator.addNote(ticket, 'engineer@polymarket.com', 'Fixed tick size validation on client side');
ErrorPatternAggregator
Deduplicate, analyze, and generate product feedback from error patterns.
import { ErrorPatternAggregator } from 'polymarket-api-toolkit';
const aggregator = new ErrorPatternAggregator();
// Add error events
aggregator.addEvent({
id: crypto.randomUUID(),
timestamp: Date.now(),
errorMessage: 'PRICE_TOO_LOW: price must be >= 0.01',
pattern: aggregator.detectPattern('PRICE_TOO_LOW'),
category: 'order_issue',
severity: 'P3',
userId: '0x...',
});
// ── Query patterns ────────────────────────────────────────────
const all = aggregator.getPatterns(); // ErrorPattern[]
const top = aggregator.getTopPatterns(10); // Top 10 by occurrence
const byCategory = aggregator.getPatternsByCategory('api_failure');
const forUser = aggregator.getPatternsForUser('0x...');
const critical = aggregator.getCriticalPatterns(); // P0 + P1 only
// ── Pattern properties ───────────────────────────────────────
// Each ErrorPattern has: patternId, errorMessage, pattern, occurrences,
// firstSeen, lastSeen, affectedUsers[], affectedSystems[], severity,
// category, relatedTickets[], suggestedFix, trend ('stable'|'increasing'|'decreasing')
// ── Summary report ───────────────────────────────────────────
const report = aggregator.generateSummaryReport();
// { totalErrors, totalPatterns, criticalPatterns, topPatterns,
// categoryBreakdown, severityBreakdown, suggestedPriorities[] }
// ── Product feedback ─────────────────────────────────────────
const feedback = aggregator.generateProductFeedback();
// [{ issue, impact, recommendation, priority: 'HIGH'|'MEDIUM'|'LOW', category }]
// ── CSV export for analytics ─────────────────────────────────
const csv = aggregator.exportCSV();
// header: pattern_id,error_message,occurrences,severity,category,first_seen,last_seen,affected_users,suggested_fix
// ── Pattern normalization ────────────────────────────────────
// detectPattern() normalizes messages by replacing:
// UUIDs → <UUID>, Ethereum addresses → <ADDRESS>, hashes → <HASH>, numbers → <N>
// So "user 0xabcd failed" and "user 0xefgh failed" bucket together
IncidentCommunicator
Draft customer-facing communications during incidents.
import { IncidentCommunicator } from 'polymarket-api-toolkit';
const communicator = new IncidentCommunicator();
// Draft different formats
const email = communicator.draftIncidentNotification(incident, 'email');
const dashboard = communicator.draftIncidentNotification(incident, 'dashboard');
const statusPage = communicator.draftIncidentNotification(incident, 'status_page');
// Mid-incident update
const update = communicator.draftIncidentUpdate(incident, 'Root cause identified');
// Resolution notice
const resolution = communicator.draftResolutionNotice(incident, 'Fixed: increased pool size');
// Impact calculation
const impact = communicator.calculateImpact(incident, 100000);
// { userPercentage: '12.5%', severityLabel: 'High', communicationFrequency: 'every_15_min' }
// Post-incident report
const report = communicator.draftPostIncidentReport(incident, rootCause, lessonsLearned);
Configuration
Source: src/config/endpoints.ts
Environment variables
| Variable | Purpose |
|---|---|
POLYMARKET_API_KEY |
CLOB API key |
POLYMARKET_API_SECRET |
CLOB API secret |
POLYMARKET_PASSPHRASE |
CLOB passphrase |
POLYGON_RPC_URL |
Override default Polygon RPC |
Endpoint configuration
import { PolymarketEndpoints, ChainConfig, TokenConfig, ContractConfig, ClobConfig, ApiErrorCodes } from 'polymarket-api-toolkit';
// CLOB endpoints
PolymarketEndpoints.CLOB.baseUrl // 'https://clob.polymarket.com'
PolymarketEndpoints.CLOB.health // '/health'
PolymarketEndpoints.CLOB.midPrice(tokenID) // '/midprice/<tokenID>'
PolymarketEndpoints.CLOB.orderbook(tokenID) // '/orderbook/<tokenID>'
// Data API
PolymarketEndpoints.DATA.baseUrl // Polymarket Data API base
PolymarketEndpoints.DATA.markets // '/markets'
PolymarketEndpoints.DATA.events // '/events'
// Gamma API
PolymarketEndpoints.GAMMA.baseUrl // Polymarket Gamma API base
PolymarketEndpoints.GAMMA.markets // '/markets'
PolymarketEndpoints.GAMMA.conditions // '/conditions'
// WebSocket
PolymarketEndpoints.WS.baseUrl // 'wss://clob.polymarket.com'
// Chain config
ChainConfig.polygon.rpcUrl // Default Polygon RPC
ChainConfig.polygon.blockExplorer // 'https://polygonscan.com'
// Token config
TokenConfig.USDCE // '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'
TokenConfig.USDC // Native USDC on Polygon
// API error codes
ApiErrorCodes.INVALID_API_CREDS // 'INVALID_API_CREDS'
ApiErrorCodes.PRICE_TOO_LOW // 'PRICE_TOO_LOW'
ApiErrorCodes.RATE_LIMITED // 'RATE_LIMITED'
// ... full list in source
Type Definitions
Source: src/config/schemas.ts exports Zod schemas for all response types. Key types re-exported from src/index.ts:
import type { Event, Market, Condition, OrderbookEntry, OrderbookSnapshot,
Trade, Candle, MidpriceResponse, SpreadResponse, OrderResponse,
Position, UserTrade, HolderData, OpenInterest, TransactionReceipt,
BalanceResponse, OrderSide, OrderType, MarketStatus, PolymarketConfig }
from 'polymarket-api-toolkit';
CLI Commands
Source: src/cli/index.ts — binary: pma
# Health check across CLOB API and Data API
pma health
# Market lookup by ID (optionally verbose)
pma market <MARKET_ID> [--verbose]
# Balance reconciliation across 3 sources
pma balance <ADDRESS> [--clob <balance>] [--data-api <balance>]
# Deposit investigation
pma deposit <ADDRESS> --expected <amount> --clob-balance <amount> [--tx-hash <hash>]
# Generate bug report from CLI
pma ticket --title "<title>" --category <api_failure|order_issue|deposit_problem|balance_discrepancy> --description "<desc>" --expected "<expected>" --actual "<actual>" --user <address> --steps "<step1,step2,step3>"
# Error pattern analysis
pma patterns [--file <log_path>] [--top <n>]
# Market maker health check
pma mm-health <TOKEN_ID>
# Position lookup
pma positions <ADDRESS> [--tokens <tokenID1,tokenID2>]
# API failure debugging
pma debug-api <URL> [--method <GET|POST|DELETE>]
# Interactive REPL mode
pma interactive
Project Structure
polymarket-api-toolkit/
├── src/
│ ├── index.ts # Barrel exports (all modules)
│ ├── config/
│ │ ├── index.ts # Re-exports endpoints, config, schemas, types
│ │ ├── endpoints.ts # API base URLs, endpoint builders, Zod schemas
│ │ └── schemas.ts # Zod validation schemas for all API responses
│ ├── clob-client/
│ │ ├── client.ts # ClobClient, DataApiClient, GammaApiClient, ClobWebSocketClient
│ │ ├── order-manager.ts # OrderManager (DRAFT → PRESIGNED → BROADCASTING → SETTLED)
│ │ ├── orderbook-tracker.ts # OrderbookTracker
│ │ └── ws-subscriber.ts # ClobWebSocketClient (reconnect, heartbeat, routing)
│ ├── blockchain/
│ │ ├── index.ts # Re-exports all blockchain modules
│ │ ├── polygon-tracer.ts # PolygonTracer (tx tracing, event decoding)
│ │ ├── usdc-tracker.ts # USDCTracker (balance, approval, CTF balances)
│ │ ├── position-lookup.ts # PositionLookup (cross-source reconciliation)
│ │ ├── bridge-tracer.ts # BridgeTracer (multi-chain deposit/withdrawal)
│ │ └── onchain-order-tracker.ts# OnChainOrderTracker
│ ├── escalation/
│ │ ├── index.ts # Re-exports escalation modules
│ │ ├── ticket-generator.ts # TicketGenerator (structured bug reports)
│ │ ├── error-pattern-aggregator.ts # ErrorPatternAggregator (dedup + analysis)
│ │ └── incident-communicator.ts # IncidentCommunicator (customer comms)
│ ├── troubleshooting/
│ │ ├── index.ts # Re-exports troubleshooting modules
│ │ ├── balance-reconcile.ts # BalanceReconciler (on-chain vs CLOB vs Data API)
│ │ ├── deposit-discrepancy.ts # DepositDiscrepancyTroubleshooter
│ │ ├── market-maker-debug.ts # MarketMakerDebugger
│ │ ├── position-lookup-cli.ts # PositionLookupCli (CLI wrapper)
│ │ └── api-failure-debug.ts # ApiFailureDebugger
│ └── cli/
│ └── index.ts # pma CLI (Commander.js)
├── tests/regression/
│ ├── balance-inconsistency.ts # Balance reconciliation edge cases
│ ├── deposit-discrepancy.ts # Deposit flow failure modes
│ ├── order-placement-failures.ts # Order rejection scenarios
│ └── ws-disconnect-resilience.ts # WS reconnection behavior
├── config/
│ ├── .env.example # Required env vars
│ └── default.json # Default configuration
├── Dockerfile # Container build
├── package.json # npm package (bin: "pma")
├── tsconfig.json # TypeScript config
├── vitest.config.ts # Test configuration
└── README.md # Full documentation
Regression Tests
Tests cover real escalation scenarios, not unit stubs:
-
balance-inconsistency.ts— Tests when on-chain, CLOB, and Data API balances diverge -
deposit-discrepancy.ts— Tests wrong USDC type, insufficient confirmations, bridge delays -
order-placement-failures.ts— Tests PRICE_TOO_LOW, TICK_SIZE_VIOLATION, MIN_NOTIONAL -
ws-disconnect-resilience.ts— Tests auto-reconnect with backoff
Run: npm run test
Getting Started
git clone https://github.com/JulianMartinez/polymarket-api-toolkit.git
cd polymarket-api-toolkit
cp config/.env.example .env
# Edit .env with your API keys
npm install
npm run typecheck
npm run test
npm run build
Configuration files:
-
config/.env.example— Environment variable template -
config/default.json— Default API endpoints and chain config
Dependencies: viem (blockchain), Axios (HTTP), ws (WebSocket), Zod (validation), commander (CLI), pino (logging)
What's Next
- Extended multi-chain support: Arbitrum, Base, Ethereum (currently Polygon-only)
- FIX protocol support for institutional market makers
- Prometheus metrics integration (prom-client dependency already wired up in package.json)
- More SDK integration tests against live Polymarket endpoints
Julian Martinez — Senior Full Stack Engineer / Developer Relations Engineer with 5+ years in Web3 (OKX, Stellar, Avalanche). B.S. Software Development. Bilingual English/Indonesian. 80+ merged PRs across 12+ open-source projects.
Tags: #typescript #web3 #polymarket #blockchain #prediction-markets #clob #polygon #api #open-source #trading
Top comments (3)
Wonderful👍 Julian, I came across your Polymarket API Toolkit and was genuinely impressed. Most SDKs stop at API wrappers, but yours extends into troubleshooting, reconciliation, incident tooling, and operational workflows—that says a lot about how you approach developer experience.
I'm curious—when you build something this comprehensive, what motivates you more: solving technically difficult infrastructure problems, or creating tools that become the standard foundation other developers rely on?
I’d say the latter drives me, though it doesn’t always start that way. Honestly it’s a little selfish, I want to prove you can build something really useful from the gold hiding in API docs. Engineers put a ton of work into these APIs, so using what they’ve given us just feels right. It’s about respecting what they have built for us more than anything else.
If developers find it useful then that's righteous, but I build these things because that's what I am, a builder.
I like that perspective. It comes across in your work that you're not just wrapping endpoints—you've put real thought into reliability, operational edge cases, and making the toolkit something developers can confidently build on in production.