In breve

Polymarket mette a disposizione tre API pubbliche (un’API consente ai programmi di leggere dati o inserire operazioni automaticamente): CLOB (trading), Gamma (individuazione dei mercati) e Data (analisi). L’SDK Python ufficiale è py-clob-client 0.34.6. L’autenticazione usa una API key + firma ECDSA e gli ordini vengono firmati tramite EIP-712 attraverso un proxy wallet su Polygon. I limiti di frequenza arrivano a circa 60 ordini al minuto per chiave. Il principale ostacolo per chi sviluppa per la prima volta è il problema di mappatura condition_id → token_id tra Gamma e CLOB. Risolvi prima questo aspetto e il resto diventa molto più semplice. Su Polymarket vengono guadagnati circa 40 milioni di dollari al mese tra ricompense di liquidità e spread intercettato dai bot, quasi interamente da utenti API.

Parte 1: le tre API

Polymarket suddivide le attività in modo chiaro tra tre servizi separati. Usare l’API corretta per ogni compito mantiene il bot veloce, semplice e nei limiti di frequenza.

APIBase URLPurposeAuth Required
CLOB APIclob.polymarket.comInserire, annullare e monitorare ordini. Leggere gli order book. Interrogare le posizioni.Yes (per il trading)
Gamma APIgamma-api.polymarket.comEsplorare i mercati, recuperare metadati, immagini, prezzi degli esiti, volume, scadenza e tag.No (pubblica)
Data APIdata-api.polymarket.comOperazioni storiche, snapshot delle posizioni, analisi utente e dati della classifica.No (pubblica)

Un ciclo tipico di un bot usa Gamma per trovare i mercati, CLOB per recuperare gli order book e inserire operazioni, e Data per eseguire backtest offline delle prestazioni della strategia. Pensa a Gamma come al “catalogo”, a CLOB come all’“exchange” e a Data come al “magazzino dati”.

Parte 2: autenticazione e modello di proxy wallet

Polymarket non firma le operazioni con la chiave privata del tuo wallet principale. Usa invece un proxy wallet in stile Gnosis Safe. Il tuo wallet principale autorizza un proxy, che esegue tutte le operazioni su Polygon. Il tuo bot API comunica con quel proxy.

Cosa ti serve

  • API key - generala in Polymarket Settings → Developer
  • Chiave privata - la chiave del tuo wallet di trading (NON la seed phrase del tuo MetaMask principale)
  • Funder address - l’indirizzo del tuo proxy wallet (visibile in Settings → Wallet)
  • Chain ID - 137 (Polygon mainnet)
  • Signature type - 1 (POLY_PROXY, standard per gli utenti retail)

Parte 3: installazione di py-clob-client

L'SDK Python ufficiale è il modo più rapido per passare da zero al tuo primo ordine. Useremo la versione 0.34.6: la release attuale su PyPI (febbraio 2026) e quella usata da quasi tutti i bot in produzione.

# Create a virtual environment first
python3 -m venv venv
source venv/bin/activate   # macOS/Linux
venv\Scripts\activate      # Windows

# Install the SDK
pip install py-clob-client==0.34.6 requests websocket-client python-dotenv

Configurazione di base del client

import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON

load_dotenv()

client = ClobClient(
    host="https://clob.polymarket.com",
    key=os.environ["POLY_PRIVATE_KEY"],
    chain_id=POLYGON,          # 137
    signature_type=1,          # POLY_PROXY
    funder=os.environ["POLY_FUNDER"],
)

# One-time: derive and cache API credentials
client.set_api_creds(client.create_or_derive_api_creds())

La chiamata create_or_derive_api_creds() firma un messaggio con la tua chiave privata. In cambio ottiene una chiave API, un secret e una passphrase. Salvali nella cache del tuo .env dopo la prima esecuzione, così non richiami l'endpoint di derivazione a ogni avvio.

Parte 4: individuare i mercati tramite Gamma

Prima di poter fare trading, devi trovare mercati su cui valga la pena operare. Gamma restituisce JSON con tutto ciò che mostra l'interfaccia di Polymarket: domanda, esiti, prezzi, volume nelle ultime 24 ore, scadenza, tag e immagini.

import requests

resp = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={
        "active": "true",
        "closed": "false",
        "tag_slug": "politics",
        "limit": 20,
        "order": "volume24hr",
        "ascending": "false",
    },
    timeout=10,
)
resp.raise_for_status()
markets = resp.json()

for m in markets:
    print(f"{m['slug']:50} Yes ${float(m['outcomePrices'][0]):.3f}  Vol24h ${m.get('volume24hr', 0):,.0f}")

Parametri di query Gamma utili

ParameterWhat it does
tag_slugFiltra per categoria (politica, sport, crypto, cultura, ecc.)
active=trueSolo mercati che accettano operazioni in questo momento
closed=falseNasconde i mercati già risolti
order=volume24hrOrdina per volume recente (segnale di liquidità)
end_date_minData ISO: esclude i mercati che si risolvono troppo presto
limitFino a 500 per pagina (usa offset per la paginazione)

Parte 5: mappatura condition_id → token_id

Questo è il problema principale nella creazione di bot per Polymarket. Gamma restituisce un condition_id (uno per mercato). Le operazioni CLOB usano un token_id (uno per esito). Ti servono sempre entrambi.

# Each Gamma market object contains 'clobTokenIds' - a JSON string array
import json

market = markets[0]
token_ids = json.loads(market['clobTokenIds'])   # ['7410...', '1120...']
yes_token = token_ids[0]   # First outcome
no_token  = token_ids[1]   # Second outcome

# Alternative: ask CLOB directly using condition_id
info = client.get_market(condition_id=market['conditionId'])
yes_token = info['tokens'][0]['token_id']

Attenzione all'ordine degli esiti

L'array outcomes di Gamma e l'array clobTokenIds hanno indici corrispondenti. Leggi sempre l'etichetta dell'esito. Non dare per scontato che l'indice 0 sia "Yes". Nei mercati con più esiti (NegRisk, Oscar, elezioni), l'indice 0 potrebbe essere "Kamala Harris" o "Taylor Swift". L'ordine è fisso, ma specifico per ogni mercato.

Parte 6: leggere gli order book

book = client.get_order_book(token_id=yes_token)

best_bid = float(book.bids[0].price) if book.bids else None
best_ask = float(book.asks[0].price) if book.asks else None
mid      = (best_bid + best_ask) / 2 if best_bid and best_ask else None
spread   = best_ask - best_bid if best_bid and best_ask else None

print(f"Bid {best_bid}  Ask {best_ask}  Mid {mid:.4f}  Spread {spread:.4f}")

Gli order book vengono restituiti come array ordinati (bid in ordine decrescente, ask in ordine crescente). Ogni livello ha un price e una size. Per stimare lo slippage su un ordine più grande, scorri il book e somma il nozionale finché non hai riempito la dimensione target.

Parte 6b: endpoint REST CLOB v2 (grezzi, senza SDK)

L'SDK incapsula questi endpoint, ma conoscerli nella forma grezza ti permette di fare debug, usare un altro linguaggio o creare un client leggero. URL di base: https://clob.polymarket.com. Tutte le letture qui sotto sono pubbliche: non serve autenticazione. Sono state verificate live a giugno 2026.

EndpointMethodWhat it returns
/marketsGETTutti i mercati, con paginazione tramite next_cursor. Include condition_id, tokens[], minimum_tick_size, neg_risk.
/sampling-marketsGETSolo i mercati con un order book attivo: il modo più rapido per trovare token_id negoziabili.
/book?token_id=GETOrder book completo: bids[] e asks[] con prezzo e quantità.
/price?token_id=&side=buyGETPrezzo migliore per un lato. side non distingue tra maiuscole e minuscole (buy/BUY). Restituisce {"price":"0.14"}.
/midpoint?token_id=GET{"mid":"0.21"}: il punto medio tra il miglior bid e il miglior ask.
/spread?token_id=GET{"spread":"0.14"}: miglior ask meno miglior bid.
/tick-size?token_id=GET{"minimum_tick_size":0.01}: il più piccolo incremento di prezzo consentito per quel token.
/prices-history?market=&interval=GETPunti di prezzo storici. interval = 1m,1h,6h,1d,1w,max.
/tradesGETScambi recenti (con autenticazione per i tuoi; pubblici per il mercato).
/orderPOSTInserisce un ordine firmato (autenticazione richiesta).
/orderDELETEAnnulla un ordine per id (autenticazione).
/ordersGETI tuoi ordini aperti ancora presenti nel book (autenticazione).
/balance-allowance?asset_type=GETIl tuo saldo pUSD e l'allowance on-chain (autenticazione). Da verificare prima di ogni ordine.

Risposte verificate, direttamente dalla API live:

$ curl "https://clob.polymarket.com/price?token_id=7347...&side=buy"
{"price":"0.14"}

$ curl "https://clob.polymarket.com/midpoint?token_id=7347..."
{"mid":"0.21"}

$ curl "https://clob.polymarket.com/spread?token_id=7347..."
{"spread":"0.14"}

$ curl "https://clob.polymarket.com/tick-size?token_id=7347..."
{"minimum_tick_size":0.01}

Header di autenticazione L2 (per REST diretto senza SDK)

Gli endpoint di lettura sono pubblici. Per inserire o annullare ordini tramite REST diretto, devi firmare ogni richiesta con le tue credenziali API. Lo SDK lo fa per te; ecco cosa genera internamente:

HeaderWhat it carries
POLY_ADDRESSL'indirizzo del wallet con cui firmi
POLY_API_KEYLa chiave API ottenuta da create_or_derive_api_creds()
POLY_PASSPHRASELa passphrase ottenuta dalla stessa chiamata di derivazione
POLY_TIMESTAMPSecondi UNIX correnti (devono essere allineati all'orologio del server: vedi il suggerimento sulla sincronizzazione dell'orologio)
POLY_NONCENonce per singola richiesta
POLY_SIGNATUREHMAC-SHA256 di timestamp + method + path + body, con la tua API secret come chiave, codificato in base64-url

Parte 7: inserire ordini: acquisto e vendita

Ordine limit (GTC: impostazione predefinita)

from py_clob_client.clob_types import OrderArgs, OrderType

args = OrderArgs(
    token_id=yes_token,
    price=0.45,
    size=100,           # Shares, not dollars. 100 shares @ $0.45 = $45 max cost.
    side="BUY",
)
signed_order = client.create_order(args)
response = client.post_order(signed_order, OrderType.GTC)
print(response)

La chiamata create_order firma un messaggio strutturato EIP-712 con la tua chiave privata. Poi post_order lo invia a CLOB. Non invii mai chiavi private in chiaro in rete: solo ordini firmati.

Allinea prima il prezzo al tick

Il prezzo di ogni ordine deve essere un multiplo esatto del minimum_tick_size del mercato (0,01 nella maggior parte dei mercati, 0,001 in quelli con spread più stretti). Un prezzo non allineato al tick viene rifiutato. Recupera il tick una volta e arrotonda di conseguenza:

from py_clob_client.clob_types import OrderArgs, OrderType

tick = float(client.get_tick_size(token_id=yes_token))   # e.g. 0.01
def to_tick(p, tick): return round(round(p / tick) * tick, 4)

price = to_tick(0.453, tick)   # -> 0.45 on a 0.01 market

Acquisto

side="BUY", size è espressa in shares (non in dollari). 100 shares a 0,45 $ costano al massimo 45 $ e pagano 100 $ se l'esito vince. Il valore minimo dell'ordine è circa 1 $.

buy = OrderArgs(token_id=yes_token, price=to_tick(0.45, tick), size=100, side="BUY")
resp = client.post_order(client.create_order(buy), OrderType.GTC)
print(resp)   # {'success': True, 'orderID': '0x...', 'status': 'live', ...}

Vendita

Per vendere si usa la stessa chiamata con side="SELL". Puoi vendere solo le quote che possiedi già: se provi a vendere più della tua posizione, l’ordine viene rifiutato con un errore "insufficient balance". Per chiudere una posizione, vendi lo stesso token_id che hai comprato.

sell = OrderArgs(token_id=yes_token, price=to_tick(0.62, tick), size=100, side="SELL")
resp = client.post_order(client.create_order(sell), OrderType.GTC)

Parametri dell’ordine in sintesi

FieldMeaningNotes
token_idL’esito su cui stai facendo tradingNon condition_id: vedi Parte 5
sideBUY o SELLBUY richiede pUSD; SELL richiede quote
price0,001-0,999Deve essere un multiplo della dimensione del tick
sizeNumero di quoteValore minimo dell’ordine circa 1 $; costo = price x size
tipo di ordineGTC / GTD / FOK / FAKPassato a post_order(...)

Mettiamo tutto insieme: il tuo primo trade via API (uno script eseguibile)

Questo è il flusso completo dall’inizio alla fine: connessione, ricerca di un mercato liquido, lettura del book, allineamento al tick, inserimento di un piccolo ordine reale e poi cancellazione. Inserisci i tuoi due secret ed eseguilo. Al primo avvio, parti con una size molto piccola, pari a pochi dollari.

import os, json, requests
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
from py_clob_client.clob_types import OrderArgs, OrderType

load_dotenv()

# 1) Connect (signing key in your EOA, funds in your proxy/funder)
client = ClobClient(
    "https://clob.polymarket.com",
    key=os.environ["POLY_PRIVATE_KEY"],
    chain_id=POLYGON,            # 137
    signature_type=1,            # 1 = email/Magic proxy, 2 = browser-wallet proxy
    funder=os.environ["POLY_FUNDER"],
)
client.set_api_creds(client.create_or_derive_api_creds())   # cache these after first run

# 2) Find the most-traded open market (Gamma, no auth)
m = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"active": "true", "closed": "false", "order": "volume24hr",
            "ascending": "false", "limit": 1}, timeout=10,
).json()[0]
token_id = json.loads(m["clobTokenIds"])[0]   # index 0 = first outcome (read the label!)
print("Trading:", m["question"])

# 3) Read the book + the tick size
tick = float(client.get_tick_size(token_id))
book = client.get_order_book(token_id)
best_ask = float(book.asks[0].price)
print("best ask", best_ask, "| tick", tick)

# 4) Place a small BUY at the ask (tiny size to start)
price = round(round(best_ask / tick) * tick, 4)      # snap to tick
order = OrderArgs(token_id=token_id, price=price, size=5, side="BUY")  # 5 shares
resp = client.post_order(client.create_order(order), OrderType.GTC)
print(resp)                                          # {'success': True, 'orderID': '0x...', ...}

# 5) Cancel it (clean up)
# client.cancel(order_id=resp["orderID"])

Tipi di ordine

TypeCodeBehaviourWhen to use
Good Till CancelledGTCRimane nel book finché non viene eseguito o finché non lo cancelliPredefinito. La maggior parte delle strategie di market making e con ordini limite.
Good Till DateGTDSi cancella automaticamente a un timestamp specificatoEvent-driven: "cancella 5 min prima della pubblicazione della Fed"
Fill or KillFOKDeve eseguire immediatamente l’intera size, altrimenti viene cancellato del tuttoGambe di arbitraggio in cui le esecuzioni parziali compromettono il trade
Fill and KillFAKEsegue tutto ciò che può al prezzo limite e cancella il restoTaking aggressivo: si comporta come un ordine a mercato con limite di prezzo

Cancellazione

# Single order
client.cancel(order_id="0xabc...")

# Cancel all orders on a specific market
client.cancel_market_orders(market=market['conditionId'])

# Nuclear option: cancel everything
client.cancel_all()

Parte 8: streaming WebSocket

Interrogare Gamma ogni secondo è inefficiente e ti farà raggiungere rapidamente i rate limit. Il feed WebSocket trasmette in tempo reale aggiornamenti del book e dei trade con latenza inferiore al secondo.

import json, websocket

WS_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

def on_open(ws):
    ws.send(json.dumps({
        "type": "market",
        "assets_ids": [yes_token, no_token],
    }))

def on_message(ws, message):
    event = json.loads(message)
    if event.get("event_type") == "price_change":
        print(f"{event['market']} {event['side']} {event['price']} size={event['size']}")

ws = websocket.WebSocketApp(
    WS_URL,
    on_open=on_open,
    on_message=on_message,
)
ws.run_forever(ping_interval=20)

Esistono due feed. Il feed /market contiene il book pubblico e i trade. Il feed /user contiene gli eventi relativi ai tuoi ordini e alle tue esecuzioni (autenticato). I bot in produzione si connettono a entrambi, si riconnettono automaticamente in caso di disconnessione e considerano il WebSocket come la fonte di verità per il book corrente.

Parte 9: rate limit e backoff

Endpoint classLimitBurst
Inserimento ordini (CLOB)circa 60 al minuto per API keycirca 10 al secondo
Cancellazione ordinicirca 120 al minutocirca 20 al secondo
Letture dei dati di mercato (CLOB book)circa 300 al minutopiù alto, variabile
Gamma APIAmpio; rispetta i 429-
Messaggi WebSocketNessun limite pratico in ingresso-

Quando ricevi un HTTP 429, il server restituisce un header Retry-After. Usa un exponential backoff con jitter:

import random, time

def post_with_backoff(fn, *args, max_retries=6):
    for attempt in range(max_retries):
        try:
            return fn(*args)
        except Exception as e:
            if "429" in str(e):
                sleep = (2 ** attempt) + random.random()
                time.sleep(min(sleep, 30))
                continue
            raise
    raise RuntimeError("Too many retries")

Parte 10: un’architettura di riferimento per bot

Ogni bot Polymarket affidabile ha gli stessi sei componenti. Implementa ciascuno come modulo separato e mantienili debolmente accoppiati.

ComponentResponsibilityAPIs used
ScannerJob pianificato: recupera i market che corrispondono ai tuoi criteri (tag, volume, giorni alla scadenza)Gamma
Motore dei prezziMantiene order book locali in tempo reale via WebSocketCLOB WS
Generatore di segnaliFunzione pura: stato del book + metadati → posizione target- (in memoria)
Gestore degli ordiniConfronta gli ordini correnti con il target, inserisce/annulla il minimo necessarioCLOB REST
Gestore del rischioApplica limiti per market, limiti di perdita giornalieri e circuit breaker- (in memoria + DB)
Logger & registroSalva ogni decisione, fill e annullamento. Alimenta i report fiscali e il debugging.SQLite / Postgres

Parte 11: modalità di errore comuni

  • Rifiuto per prezzo fuori tick - il prezzo deve essere un multiplo esatto del minimum_tick_size del market. Recuperalo tramite /tick-size?token_id= e arrotonda prima di firmare, altrimenti l’ordine viene respinto.
  • 404 "No orderbook exists" - hai interrogato /book, /price o /midpoint su un token chiuso/risolto. Usa /sampling-markets per trovare token con un book attivo.
  • Dati WebSocket obsoleti - traccia l’ora dell’ultimo messaggio per asset; se non arrivano aggiornamenti per più di 30 s su un market attivo, forza un aggiornamento via REST.
  • Collisioni di nonce - py-clob-client gestisce per te i nonce degli ordini, ma se stai implementando un signer personalizzato incrementa il nonce a ogni ordine.
  • Saldo insufficiente - controlla sempre il saldo pUSD prima di inserire un ordine; il book potrebbe mostrare il tuo ordine, ma il matching lo rifiuterà.
  • Market in pausa o in risoluzione - controlla market.active && !market.closed prima di fare trading. Gli aggiornamenti di Gamma sono in ritardo di qualche secondo rispetto a CLOB durante la risoluzione.
  • Disallineamento dell’adapter NegRisk - i market multi-esito passano da un adapter NegRisk separato. L’SDK lo gestisce, ma verifica che il tuo ordine sia arrivato alla venue corretta.

Parte 12: premi di liquidità via API

Polymarket distribuisce circa 5 milioni di dollari al mese in premi di liquidità generali, più oltre 5 milioni di dollari al mese in premi specifici per lo sport (vedi Premi di liquidità). La maggior parte va ai market maker basati su API, che mantengono quotazioni strette su entrambi i lati in migliaia di market.

La formula dei premi favorisce gli ordini vicini al midpoint, tenendo conto di size e tempo sul book. Ecco un ciclo minimo di market making:

  1. Leggi l’order book del market target
  2. Calcola un midpoint equo (ad esempio, il VWAP dei primi 3 livelli su ciascun lato)
  3. Inserisci un bid a mid − spread_target/2 e un ask a mid + spread_target/2
  4. A ogni aggiornamento WebSocket, aggiorna il prezzo se la tua quotazione si discosta dal target di più di un tick
  5. Annulla ed esci se il book si assottiglia o arrivano notizie rilevanti

Parte 13: andare in produzione

  • Hosting: un VPS da 6 dollari al mese (Hetzner, DigitalOcean) in Europa o US-East è sufficiente per la maggior parte dei bot. Collocalo con l’RPC Polygon se ti serve una latenza inferiore a 10 ms.
  • RPC: usa Alchemy, Infura o QuickNode per un RPC Polygon affidabile. I piani gratuiti vanno bene finché non inserisci centinaia di ordini al minuto.
  • Monitoraggio: Prometheus + Grafana per le metriche; un bot Telegram per gli avvisi. Registra ogni ID ordine che invii e ogni fill che ricevi.
  • Backup: salva lo stato ogni minuto. Se il VPS si arresta durante un fill, devi poter riprendere in pochi secondi, non riconciliare tutto a mano.
  • Fiscalità: il logger è anche la tua traccia di audit: vedi Guida fiscale.

Parte 14 - Consigli pratici verificati per Polymarket API

Scheda rapida Situazione → Azione

SituationActionWhy
401 "invalid api key" alla prima chiamataVerifica che signature_type corrisponda all’origine del wallet e che funder sia l’indirizzo proxyLa mancata corrispondenza tra tipo 1 e 2 causa l’80% degli errori 401; il resto dipende dall’uso dell’EOA come funder
Ordini rifiutati con "insufficient balance"Interroga /balance-allowance prima di ogni ordine e prenota l’importo localmenteCLOB blocca il collaterale nel momento in cui pubblichi l’ordine; due ordini simultanei possono impegnare due volte gli stessi fondi
Throttling 429 sull’endpoint /orderUsa un backoff con jitter: 2^attempt + random(), con limite massimo di 30 sCloudflare applica throttling invece di rifiutare le richieste; un retry ingenuo aumenta la coda
WebSocket disconnesso durante un tradeRecupera uno snapshot del book via REST, riconcilia lo stato locale e poi sottoscriviti di nuovoI delta durante l’interruzione vengono persi; lo snapshot riallinea le scale di prezzo
Ordine inserito ma senza conferma di fillInterroga /data/order/{id} entro 5 s; se è in pending, attendi; se non viene trovato, sostituisciloEvento raro ma gestibile; la regola predefinita è “controlla lo stato, poi agisci”
Mercato risolto mentre una quotazione è attivaAnnulla tutti gli ordini aperti su quel conditionId all’evento di risoluzioneDopo la risoluzione, gli ordini possono restare in circolazione come fill zombie se si attivano anomalie dell’adapter
Bot di market-making in esecuzioneInserisci quotazioni entro 2 centesimi dal midpoint con size di almeno 100 shareLa formula dei premi pesa spread stretto + size + tempo nel book; vince chi resta stretto, dimensionato e presente
Bot di arbitraggio su mercati multi-outcome in esecuzioneUsa FOK per ogni leg, non GTCFill parziali sulla leg A con leg B completa = esposizione non coperta e perdita immediata
Prima volta che sviluppi un botCostruisci prima lo scanner, poi il motore di pricing, poi il segnale: mai il segnale per primoI segnali senza uno stato del book pulito sono trappole di correlazione; fai funzionare prima le pipeline
Bot di produzione in crash alle 3:00Configura riavvio automatico con systemd + alert Telegram + stato persistenteQualsiasi bot non presidiato prima o poi va in crash; il punto è se riparte in modo pulito

Passaggi successivi

  • Strumenti e risorse - dashboard di terze parti, analytics e feed di dati che integrano l’API
  • Strategie avanzate - arbitraggio multi-leg e strutture simili a opzioni adatte ai bot
  • Premi di liquidità - formule esatte per guadagnare rebate da market-making
  • Guida all’order book - intuizioni più approfondite per leggere il book prima di svilupparci sopra
  • Glossario - definizioni in linguaggio semplice di ogni termine usato in questa guida

Controllo rapido