DEV Community

Julian Martinez
Julian Martinez

Posted on

Polymarket API Toolkit - Complete TypeScript SDK for CLOB, Data API, Gamma API & Polygon Tracing

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
Enter fullscreen mode Exit fullscreen mode

CLI usage (no install needed):

npx pma health
npx pma market <MARKET_ID>
npx pma balance <ADDRESS>
npx pma ticket --title "..." --category api_failure
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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 });
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

WS subscription protocol (client sends):

{"type":"subscribe","channel":"orderbook","markets":["<tokenID>"]}
{"type":"subscribe","channel":"trades","markets":["<tokenID>"]}
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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...' }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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' }
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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[] }
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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[] }
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
hiren-kava profile image
Hiren Kava

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?

Collapse
 
j_dev28 profile image
Julian Martinez

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.

Collapse
 
hiren-kava profile image
Hiren Kava

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.