🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Polymarket API Guide

by @nwcalvin

Polymarket API and data access guide. Learn how to connect, find markets, get real-time data via WebSocket, access order books, place orders via CLOB SDK, an...

Versionv2.0.0
Downloads1,015
TERMINAL
clawhub install innotech-polymarket-api

πŸ“– About This Skill


name: innotech-polymarket-api description: Polymarket API and data access guide. Learn how to connect, find markets, get real-time data via WebSocket, access order books, place orders via CLOB SDK, and understand market mechanics.

Polymarket API & Data Access Guide

Purpose: Comprehensive reference for connecting to, understanding, and using Polymarket APIs Last Updated: 2026-03-23


🎯 What This Skill Covers

1. βœ… Connect to Polymarket - API endpoints, authentication 2. βœ… Find Markets - Search, filter, time-based pattern discovery 3. βœ… Get Real-time Data - WebSocket usage and event types 4. βœ… Access Order Books - Liquidity, bid/ask spreads, depth 5. βœ… Place Orders - CLOB SDK, market/limit orders, decimal precision 6. βœ… Market Mechanics - How 5-min markets work, winner determination, timing 7. βœ… Known Pitfalls - Data reliability, timing issues, common bugs

This skill does NOT include:

  • ❌ Trading strategies
  • ❌ Signal analysis methodology

  • πŸ—οΈ Architecture Overview

    API Components

    | Component | URL | Purpose | Auth? | |-----------|-----|---------|-------| | Gamma API | https://gamma-api.polymarket.com | Market info, prices, metadata | No | | CLOB API | https://clob.polymarket.com | Order books, trades, market structure | No | | CLOB Auth API | https://clob-auth.polymarket.com | JWT token for trading | Yes | | Data API | https://data-api.polymarket.com | Historical data, order books | No | | WebSocket | wss://ws-subscriptions-clob.polymarket.com/ws/market | Real-time price/book updates | No |

    Data Flow for 5-Minute BTC Up/Down Markets

    1. Generate timestamp β†’ slug: btc-updown-5m-{timestamp}
    2. Fetch market info from Gamma API β†’ get asset IDs
    3. Subscribe to WebSocket with asset IDs β†’ real-time bid/ask
    4. Place orders via CLOB SDK β†’ buy UP or DOWN tokens
    5. Market closes (5 min) β†’ Polymarket resolves winner
    6. Verify winner via Gamma API outcomePrices (1.0 = winner)
    


    πŸ” Finding Markets

    5-Minute Markets β€” Timestamp Pattern

    import time

    def get_current_interval(interval_seconds=300): """Calculate current 5-minute interval timestamp""" current_time = int(time.time()) return (current_time // interval_seconds) * interval_seconds

    def generate_market_timestamps(num_markets=8): """Generate N market timestamps: current + next (N-1) intervals""" base = get_current_interval() return [base + i * 300 for i in range(num_markets)]

    Supported cryptos: btc, eth, sol, xrp

    Slug pattern: {crypto}-updown-5m-{timestamp}

    Example: btc-updown-5m-1772699400

    API Search Methods

    import requests

    By slug (most reliable for 5-min markets)

    resp = requests.get("https://gamma-api.polymarket.com/markets", params={"slug": "btc-updown-5m-1772699400"})

    By keyword search

    resp = requests.get("https://gamma-api.polymarket.com/markets", params={"_s": "bitcoin"})

    Active markets

    resp = requests.get("https://gamma-api.polymarket.com/markets", params={"active": "true", "limit": 100})


    πŸ†” Asset IDs (Token IDs)

    Critical: Each outcome (UP/DOWN) has a unique asset ID required for WS subscriptions and trading.

    Getting Asset IDs

    import json

    def get_asset_ids(market): """Get asset IDs from clobTokenIds field (preferred)""" clob_token_ids_str = market.get('clobTokenIds') if clob_token_ids_str: return json.loads(clob_token_ids_str) # List of 2 IDs: [UP_ID, DOWN_ID] return []

    Important:

  • βœ… clobTokenIds is a JSON string, not an array β€” must json.loads()
  • βœ… Index 0 = UP, Index 1 = DOWN (for btc-updown markets)
  • ⚠️ Fall back to tokens[].token_id only if clobTokenIds missing

  • πŸ“‘ WebSocket β€” Real-time Data

    Connection & Subscription

    import aiohttp, asyncio, json, time

    async def monitor_market(asset_ids): async with aiohttp.ClientSession() as session: ws = await session.ws_connect( "wss://ws-subscriptions-clob.polymarket.com/ws/market", heartbeat=None, timeout=aiohttp.ClientWSTimeout(ws_close=30.0) ) # Subscribe await ws.send_json({ "assets_ids": asset_ids, "type": "market", "custom_feature_enabled": True }) # Heartbeat every 10 seconds last_ping = time.time() async for msg in ws: if msg.data == "PONG": continue if time.time() - last_ping >= 10: await ws.send_str("PING") last_ping = time.time() if msg.type == aiohttp.WSMsgType.TEXT: data = json.loads(msg.data) # Handle events...

    ⚠️ CRITICAL: Event Type Reliability

    | Event Type | Description | Reliability | Use? | |------------|-------------|-------------|------| | book | Full orderbook snapshot | ⭐⭐⭐ | βœ… YES | | best_bid_ask | Best bid/ask update | ⭐⭐⭐ | βœ… YES | | price_change | Past trade records | ⭐ | ❌ NO | | last_trade_price | Last trade price | ⭐ | ❌ NO |

    πŸ”₯ Key Lesson: price_change = past trades, NOT current orderbook state. Using it as real-time bid/ask causes impossible states (ask < bid). Only use book and best_bid_ask.

    ⚠️ CRITICAL: Book Event Structure

    # ❌ WRONG: bids/asks are NOT under 'book' key
    bids = data.get('book', {}).get('bids', [])  # EMPTY!

    βœ… CORRECT: bids/asks at TOP LEVEL

    bids = data.get('bids', []) asks = data.get('asks', [])

    Order Book Fields

    # Each bid/ask entry:
    {
        'price': '0.4550',  # Price level (0-1)
        'size': '7270'      # Size in USD at this price
    }

    Liquidity = sum of all bid sizes + sum of all ask sizes

    total_liquidity = sum(float(b.get('size', 0)) for b in bids) + sum(float(a.get('size', 0)) for a in asks)


    πŸ’° Placing Orders β€” CLOB SDK

    Authentication

    from py_clob_client.client import ClobClient
    from py_clob_client.clob_types import ApiCreds

    host = "https://clob.polymarket.com" chain_id = 137 # Polygon

    client = ClobClient(host, key=key, chain_id=chain_id) client.set_api_creds(ApiCreds( api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET", api_passphrase="YOUR_API_PASSPHRASE" ))

    πŸ”₯ CRITICAL: Decimal Precision Rules

    | Amount Type | Max Decimals | Example | |-------------|-------------|---------| | Maker amount (tokens) | 4 | 10.7419 | | Taker amount (USD) | 2 | 3.33 | | Price | 4 | 0.3100 |

    Rule: Calculate tokens FIRST, then derive USD

    # βœ… CORRECT: Explicit precision control
    raw_tokens = amount_usd / price           # e.g. 3.33 / 0.31 = 10.74193548
    tokens = round(raw_tokens, 4)             # 10.7419 (max 4 decimals)
    usd_amount = round(tokens * price, 2)     # 3.33 (max 2 decimals)
    price_rounded = round(price, 4)           # 0.3100 (max 4 decimals)

    ❌ WRONG: Let SDK auto-calculate β†’ may produce > 4 decimal tokens

    ❌ WRONG: Market order with SDK auto-calculation β†’ same issue

    Limit Order (Recommended β€” More Control)

    from py_clob_client.order_builder.constants import BUY

    order_args = OrderArgs( token_id=asset_id, price=price_rounded, # 0.3100 (max 4 decimals) size=tokens, # 10.7419 (max 4 decimals) side=BUY, expiration=0, # GTC (Good Till Cancelled) )

    A limit order at market price = instant fill with full precision control

    Market Order (From Official Docs)

    market_order = client.create_market_order(
        token_id="TOKEN_ID",
        side=BUY,
        amount=100,      # Dollar amount! SDK calculates tokens
        price=0.50,      # Slippage protection (can be 0)
        order_type=OrderType.FOK
    )
    client.post_order(market_order, OrderType.FOK)
    

    Market Order Notes:

  • amount = USD amount (SDK calculates tokens internally)
  • price = slippage protection ceiling, NOT exact execution price
  • ⚠️ SDK may produce >4 decimal tokens β†’ prefer limit orders for safety
  • ⚠️ ALWAYS Read Official Docs Before Trading

  • https://docs.polymarket.com/trading/orders/create
  • https://docs.polymarket.com/trading/orders/overview
  • Never guess API behavior β€” read docs first. Multiple failed versions were caused by not reading official documentation.


    ⏰ 5-Minute Market Mechanics

    How Markets Work

    1. New market every 5 minutes β€” timestamp-based: btc-updown-5m-{unix_ts} 2. Market duration: Exactly 5 minutes (300 seconds) 3. Winner determination: Polymarket resolves based on BTC price movement during the 5-minute interval - UP wins β†’ outcomePrices: UP=1.0, DOWN=0.0 - DOWN wins β†’ outcomePrices: UP=0.0, DOWN=1.0 4. Market appears on Polymarket: ~30-60 seconds before interval start 5. Orderbook becomes active: Shortly after market appears

    Timing β€” What Happens When

    | Time | Event | |------|-------| | T-60s | Market appears on Gamma API | | T-30s | Orderbook starts getting liquidity | | T+0 | 5-minute interval starts | | T+0 ~ T+30s | Ask prices around 0.50, active trading | | T+240s | Most trading done, prices converge | | T+300s | Market closes | | T+300s ~ T+360s | Winner being resolved | | T+360s+ | Gamma API outcomePrices shows 1.0/0.0 (resolved) |

    ⚠️ CRITICAL: Winner Resolution Timing

    # ❌ WRONG: Fetch winner immediately after market close
    

    Gamma API returns trading prices (0.60/0.40), NOT resolved prices (1.0/0.0)

    Can take 60+ seconds to resolve

    βœ… CORRECT: Wait for outcomePrices to show 1.0/0.0

    After market close, poll Gamma API until resolved

    outcomePrices [1.00, 0.00] = UP winner confirmed

    outcomePrices [0.00, 1.00] = DOWN winner confirmed


    πŸ“Š Data Reliability β€” What to Trust

    Price Data Hierarchy

    | Data Source | Use For | Accuracy | Latency | |-------------|---------|----------|---------| | book (WS) | Orderbook, bid/ask | ⭐⭐⭐ | Real-time | | best_bid_ask (WS) | Quick price check | ⭐⭐⭐ | Real-time | | outcomePrices (Gamma) | Market probability | ⭐⭐ | 5-30s delayed | | outcomePrices (Gamma, resolved) | Winner verification | ⭐⭐⭐ | 60s+ after close | | price_change (WS) | Past trades only | ❌ | N/A | | best_bid_ask ask price | Trading probability | ⚠️ | NOT true probability |

    ⚠️ best_bid_ask Ask Price β‰  Market Probability

  • Ask price = lowest sell order in orderbook
  • This is not the market's consensus probability
  • It reflects orderbook dynamics, not market sentiment
  • All versions using ask price as probability showed ~50% accuracy

  • πŸ”„ Market Rotation

    Interval-Based Rotation (Correct Method)

    def get_current_interval():
        """Get current 5-minute interval boundary"""
        current_time = int(time.time())
        return (current_time // 300) * 300

    def should_rotate(current_interval, last_interval): """Check if we've entered a new interval""" return current_interval != last_interval

    ❌ WRONG: Time-based (rotates at wrong time)

    time_since_rotation = time.time() - last_rotation

    if time_since_rotation >= 300: rotate() # Off by up to 15 seconds!

    βœ… CORRECT: Interval-based (rotates exactly when interval changes)

    if get_current_interval() != current_base_timestamp: rotate()


    ⚠️ Known Pitfalls & Lessons Learned

    1. Never Use price_change as Orderbook Data

  • price_change = past trade records
  • BUY in price_change = someone bought at this price IN THE PAST
  • This is NOT the current best ask
  • Using it causes impossible states (ask < bid)
  • 2. Decimal Precision Will Break Your Orders

  • Maker amount (tokens): max 4 decimals
  • Taker amount (USD): max 2 decimals
  • Calculate tokens first, derive USD second
  • Never trust SDK auto-calculation
  • Use limit orders for full control
  • 3. Read Official Docs Before Implementing

  • Multiple wasted versions from guessing API behavior
  • Official docs: https://docs.polymarket.com/trading/orders/create
  • 4. Winner Resolution Takes Time

  • Gamma API doesn't return 1.0/0.0 immediately after close
  • Poll until outcomePrices resolves to 1.0/0.0
  • Verify with Gamma API outcomePrices post-session
  • ⚠️ Pitfall #5: Market Resolution Timing

  • Gamma API doesn't return 1.0/0.0 immediately after close
  • Can take 60+ seconds for outcomePrices to resolve
  • Poll until you see 1.0/0.0, don't assume immediate resolution
  • 6. Market Appearance Timing is Variable

  • Markets don't always appear exactly 60s before start
  • Sometimes 30s, sometimes 90s
  • Fetch ask immediately when market appears β€” don't delay
  • 7. Ask Price at Market Start β‰ˆ 0.50

  • Fresh market ask is ~0.50 (fair odds)
  • Ask drifts quickly (within seconds) based on BTC movement
  • If you see ask = 0.99, market is already closed/ending

  • πŸ“š API Reference

    Gamma API Endpoints

    | Endpoint | Method | Description | Auth? | |----------|--------|-------------|-------| | /markets | GET | List/search markets | No | | /markets/{id} | GET | Get market by ID | No | | /markets?slug={slug} | GET | Get market by slug | No | | /markets/{id}/price | GET | Current prices | No |

    WebSocket Events

    | Event | Direction | Reliable? | Structure | |-------|-----------|-----------|-----------| | book | Sβ†’C | βœ… | bids[], asks[] at top level | | best_bid_ask | Sβ†’C | βœ… | best_bid, best_ask | | price_change | Sβ†’C | ❌ | Past trades, not orderbook | | last_trade_price | Sβ†’C | ❌ | Last trade, not orderbook |

    Rate Limits

    | API | Limit | Best Practice | |-----|-------|---------------| | Gamma API | ~100 req/min | Use WebSocket instead of polling | | Data API | ~50 req/min | Cache responses | | WebSocket | No hard limit | PING every 10s, batch subscriptions |


    πŸ”§ CLOB SDK Quick Reference

    from py_clob_client.client import ClobClient
    from py_clob_client.clob_types import OrderArgs, OrderType, ApiCreds
    from py_clob_client.order_builder.constants import BUY

    Initialize

    client = ClobClient("https://clob.polymarket.com", key=key, chain_id=137) client.set_api_creds(ApiCreds(api_key=..., api_secret=..., api_passphrase=...))

    Limit order (recommended)

    order = OrderArgs(token_id=ASSET_ID, price=0.50, size=10.0, side=BUY, expiration=0)

    size = tokens (max 4 decimals), price (max 4 decimals)

    Market order

    market_order = client.create_market_order(token_id=ASSET_ID, side=BUY, amount=100, price=0, order_type=OrderType.FOK)

    amount = USD, price = slippage ceiling

    Nonces: Must be unique per order. Use counter or UUID.

    Official Documentation

  • Orders: https://docs.polymarket.com/trading/orders/create
  • Overview: https://docs.polymarket.com/trading/orders/overview

  • πŸ“ Version History

  • v2.0.0 (2026-03-23):
  • - βœ… Added CLOB SDK trading section (limit/market orders, decimal precision) - βœ… Added 5-min market mechanics (timing, winner determination) - βœ… Added data reliability guide (what to trust, what not to) - βœ… Added known pitfalls section (7 critical lessons) - βœ… Added winner verification timing guide - βœ… Added winner verification timing guide - βœ… Added market rotation (interval-based vs time-based) - βœ… Updated WebSocket reliability table - βœ… Removed arbitrage scanner example (not applicable to 5-min markets)

  • v1.2.0 (2026-03-05):
  • - βœ… Fixed WebSocket book event structure - βœ… Added real-time liquidity tracking - βœ… Added multi-cryptocurrency monitoring

  • v1.0.0 (2026-03-03):
  • - Initial release