En bref

Polymarket met à votre disposition trois API publiques (une API permet à des programmes de lire des données ou de placer des ordres automatiquement) : CLOB (trading), Gamma (découverte des marchés) et Data (analyse). Le SDK Python officiel est py-clob-client 0.34.6. L’authentification repose sur une clé API + signature ECDSA, et les ordres sont signés via EIP-712 au moyen d’un proxy wallet Polygon. Les limites de débit vous plafonnent à environ 60 ordres par minute par clé. Le principal piège pour les nouveaux développeurs est le problème de mapping condition_id → token_id entre Gamma et CLOB. Réglez-le d’abord, et le reste devient beaucoup plus simple. Environ 40 millions de dollars par mois en récompenses de liquidité et en spread capté par des bots sont gagnés sur Polymarket, presque exclusivement par des utilisateurs de l’API.

Partie 1 : les trois API

Polymarket répartit clairement les responsabilités entre trois services distincts. Utiliser la bonne API pour chaque tâche permet de garder un bot rapide, simple et conforme aux limites de débit.

APIBase URLPurposeAuth Required
CLOB APIclob.polymarket.comPlacer, annuler et suivre des ordres. Lire les carnets d’ordres. Interroger les positions.Oui (pour trader)
Gamma APIgamma-api.polymarket.comParcourir les marchés, récupérer les métadonnées, les images, les prix des résultats, le volume, l’échéance et les tags.Non (public)
Data APIdata-api.polymarket.comTrades historiques, instantanés de positions, analyses utilisateur, données de classement.Non (public)

Une boucle de bot typique utilise Gamma pour trouver des marchés, CLOB pour récupérer les carnets d’ordres et placer des trades, et Data pour backtester les performances de la stratégie hors ligne. Voyez Gamma comme le « catalogue », CLOB comme la « place de marché » et Data comme l’« entrepôt ».

Partie 2 : authentification et modèle de proxy wallet

Polymarket ne signe pas les trades avec la clé privée de votre wallet principal. Il utilise plutôt un proxy wallet de type Gnosis Safe. Votre wallet principal autorise un proxy, et ce proxy exécute tous les trades sur Polygon. Votre bot API communique avec ce proxy.

Ce dont vous avez besoin

  • Clé API - à générer dans Polymarket Settings → Developer
  • Clé privée - la clé de votre wallet de trading (PAS la phrase de récupération de votre MetaMask principal)
  • Adresse funder - l’adresse de votre proxy wallet (affichée dans Settings → Wallet)
  • Chain ID - 137 (Polygon mainnet)
  • Type de signature - 1 (POLY_PROXY, standard pour les utilisateurs particuliers)

Partie 3 : installation de py-clob-client

Le SDK Python officiel est le moyen le plus rapide de passer de zéro à votre premier ordre. Nous utiliserons la version 0.34.6 : la version actuellement publiée sur PyPI (février 2026) et celle qu’utilisent presque tous les bots en production.

# 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

Configuration de base du 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())

L’appel create_or_derive_api_creds() signe un message avec votre clé privée. Il l’échange contre une clé API, un secret et une passphrase. Mettez-les en cache dans votre .env après la première exécution, afin de ne pas appeler l’endpoint de dérivation à chaque démarrage.

Partie 4 : découvrir les marchés via Gamma

Avant de pouvoir trader, vous devez trouver des marchés qui en valent la peine. Gamma renvoie du JSON contenant tout ce qu’affiche l’interface Polymarket : la question, les résultats, les prix, le volume sur 24 h, l’échéance, les tags et les images.

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}")

Paramètres de requête Gamma utiles

ParameterWhat it does
tag_slugFiltre par catégorie (politique, sports, crypto, culture, etc.)
active=trueUniquement les marchés qui acceptent actuellement des transactions
closed=falseMasque les marchés résolus
order=volume24hrTrie par volume récent (signal de liquidité)
end_date_minDate ISO — ignore les marchés qui se résolvent trop bientôt
limitJusqu’à 500 par page (utilisez offset pour la pagination)

Partie 5 : correspondance condition_id → token_id

C’est la principale difficulté lors de la création de bots Polymarket. Gamma renvoie un condition_id (un par marché). Les transactions CLOB utilisent un token_id (un par résultat). Vous avez toujours besoin des deux.

# 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']

Attention à l’ordre des résultats

Le tableau outcomes et le tableau clobTokenIds de Gamma sont alignés par index. Lisez toujours le libellé du résultat. Ne partez pas du principe que l’index 0 correspond à "Yes". Dans les marchés à résultats multiples (NegRisk, Oscars, élections), l’index 0 peut être "Kamala Harris" ou "Taylor Swift". L’ordre est fixe, mais propre à chaque marché.

Partie 6 : lire les carnets d’ordres

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}")

Les carnets d’ordres sont renvoyés sous forme de tableaux triés (offres d’achat décroissantes, offres de vente croissantes). Chaque niveau comporte un price et une size. Pour estimer le slippage sur un ordre plus important, parcourez le carnet et additionnez le notionnel jusqu’à atteindre la taille cible.

Partie 6b : les endpoints REST CLOB v2 (bruts, sans SDK)

Le SDK les encapsule, mais connaître les endpoints bruts vous permet de déboguer, d’utiliser un autre langage ou de créer un client léger. URL de base : https://clob.polymarket.com. Toutes les lectures ci-dessous sont publiques — aucune authentification requise. Elles ont été vérifiées en production en juin 2026.

EndpointMethodWhat it returns
/marketsGETTous les marchés (paginés via next_cursor). Inclut condition_id, tokens[], minimum_tick_size, neg_risk.
/sampling-marketsGETUniquement les marchés avec un carnet d’ordres actif : le moyen le plus rapide de trouver des token_id négociables.
/book?token_id=GETCarnet d’ordres complet : bids[] et asks[] avec prix + taille.
/price?token_id=&side=buyGETMeilleur prix pour un côté. side n’est pas sensible à la casse (buy/BUY). Renvoie {"price":"0.14"}.
/midpoint?token_id=GET{"mid":"0.21"} : à mi-chemin entre la meilleure offre d’achat et la meilleure offre de vente.
/spread?token_id=GET{"spread":"0.14"} : meilleure offre de vente moins meilleure offre d’achat.
/tick-size?token_id=GET{"minimum_tick_size":0.01} : le plus petit pas de prix autorisé pour ce token.
/prices-history?market=&interval=GETPoints de prix historiques. interval = 1m,1h,6h,1d,1w,max.
/tradesGETTrades récents (authentification pour les vôtres ; public pour le marché).
/orderPOSTPlacer un ordre signé (authentification requise).
/orderDELETEAnnuler un ordre par id (authentification).
/ordersGETVos ordres ouverts en attente (authentification).
/balance-allowance?asset_type=GETVotre solde USDC et votre autorisation on-chain (authentification). À vérifier avant chaque ordre.

Réponses vérifiées, directement depuis l’API en production :

$ 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}

En-têtes d’authentification L2 (pour le REST brut sans le SDK)

Les endpoints de lecture sont publics. Pour placer ou annuler des ordres via REST brut, vous signez chaque requête avec vos identifiants d’API. Le SDK s’en charge pour vous ; voici ce qu’il construit en interne :

HeaderWhat it carries
POLY_ADDRESSL’adresse de votre portefeuille de signature
POLY_API_KEYLa clé d’API issue de create_or_derive_api_creds()
POLY_PASSPHRASELa phrase secrète issue du même appel de dérivation
POLY_TIMESTAMPSecondes UNIX actuelles (doit correspondre à l’horloge du serveur ; voir le conseil sur la synchronisation de l’horloge)
POLY_NONCENonce propre à chaque requête
POLY_SIGNATUREHMAC-SHA256 de timestamp + method + path + body, calculé avec votre secret d’API comme clé, encodé en base64 URL-safe

Partie 7 : placer des ordres — acheter et vendre

Ordre limite (GTC : valeur par défaut)

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)

L’appel create_order signe un message structuré EIP-712 avec votre clé privée. Ensuite, post_order l’envoie à CLOB. Vous n’envoyez jamais de clés privées brutes sur le réseau : uniquement des ordres signés.

Commencez par aligner votre prix sur le pas de cotation

Chaque prix d’ordre doit être un multiple exact du minimum_tick_size du marché (0,01 sur la plupart des marchés, 0,001 sur les plus serrés). Un prix non conforme au pas est rejeté. Récupérez le pas une fois, puis arrondissez en conséquence :

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

Achat

side="BUY", size est exprimé en parts (pas en dollars). 100 parts à 0,45 $ coûtent au maximum 45 $ et rapportent 100 $ si le résultat est gagnant. Le montant minimal d’un ordre est d’environ 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', ...}

Vente

La vente utilise le même appel avec side="SELL". Vous ne pouvez vendre que des parts que vous détenez déjà : toute vente supérieure à votre position est rejetée avec une erreur "insufficient balance". Pour solder une position, vendez le même token_id que celui que vous avez acheté.

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)

Paramètres d’ordre en un coup d’œil

FieldMeaningNotes
token_idLe résultat que vous négociezPas condition_id : voir la partie 5
sideBUY ou SELLBUY nécessite USDC ; SELL nécessite des parts
price0.001-0.999Doit être un multiple du pas de cotation
sizeNombre de partsValeur minimale d’ordre d’environ 1 $ ; coût = price x size
type d’ordreGTC / GTD / FOK / FAKTransmis à post_order(...)

Mise en pratique : votre première transaction via l’API (un script exécutable)

Voici le flux complet de bout en bout : connexion, recherche d’un marché liquide, lecture du carnet, alignement sur le pas de cotation, placement d’un petit ordre réel, puis annulation. Renseignez vos deux secrets et exécutez le script. Pour votre tout premier lancement, commencez avec une très petite taille (quelques dollars).

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"])

Types d’ordres

TypeCodeBehaviourWhen to use
Good Till CancelledGTCReste dans le carnet jusqu’à exécution ou annulation de votre partPar défaut. La plupart des stratégies de market making et d’ordres limit.
Good Till DateGTDS’annule automatiquement à un horodatage spécifiéPiloté par événement : « annuler 5 min avant la publication de la Fed »
Fill or KillFOKDoit exécuter immédiatement toute la taille, sinon l’ordre est entièrement annuléOpérations d’arbitrage où les exécutions partielles compromettent la transaction
Fill and KillFAKExécute ce qui est possible au prix limit, annule le restePrise de liquidité agressive : agit comme un ordre au marché avec prix plafond

Annulation

# 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()

Partie 8 : streaming WebSocket

Interroger Gamma toutes les secondes est inefficace, et vous atteindrez vite les limites de débit. Le flux WebSocket diffuse en temps réel les mises à jour du carnet d’ordres et des transactions, avec une latence inférieure à la seconde.

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)

Deux flux existent. Le flux /market fournit le carnet d’ordres public et les transactions. Le flux /user fournit vos propres événements d’ordres et d’exécutions (authentifié). Les bots de production se connectent aux deux, se reconnectent automatiquement en cas de déconnexion et considèrent le WebSocket comme la source de vérité pour le carnet courant.

Partie 9 : limites de débit et backoff

Endpoint classLimitBurst
Placement d’ordres (CLOB)environ 60 / minute par clé APIenviron 10 / seconde
Annulation d’ordresenviron 120 / minuteenviron 20 / seconde
Lectures de données de marché (carnet CLOB)environ 300 / minuteplus élevé, variable
Gamma APIGénéreuse ; respectez les 429-
Messages WebSocketAucune limite pratique en entrée-

Lorsque vous recevez une réponse HTTP 429, le serveur renvoie un en-tête Retry-After. Utilisez un backoff exponentiel avec 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")

Partie 10 : architecture de bot de référence

Tout bot Polymarket fiable comporte les mêmes six composants. Construisez chacun comme un module séparé et gardez-les faiblement couplés.

ComponentResponsibilityAPIs used
AnalyseurTâche planifiée : récupère les marchés correspondant à vos critères (tags, volume, jours avant expiration)Gamma
Moteur de prixMaintient des carnets d’ordres locaux en temps réel via WebSocketCLOB WS
Générateur de signauxFonction pure : état du carnet + métadonnées → position cible- (en mémoire)
Gestionnaire d’ordresCompare les ordres actuels à la cible, puis place/annule le minimum nécessaireCLOB REST
Gestionnaire des risquesApplique des plafonds par marché, des limites de perte quotidienne et des coupe-circuits- (en mémoire + DB)
Journalisation et registreConserve chaque décision, exécution et annulation. Alimente les rapports fiscaux et le débogage.SQLite / Postgres

Partie 11 : Modes de défaillance courants

  • Rejet d’un prix hors tick - le prix doit être un multiple exact du minimum_tick_size du marché. Récupérez-le via /tick-size?token_id= et arrondissez avant de signer, sinon l’ordre sera rejeté.
  • 404 "No orderbook exists" - vous avez interrogé /book, /price ou /midpoint sur un token fermé ou résolu. Utilisez /sampling-markets pour trouver des tokens avec un carnet actif.
  • Données WebSocket obsolètes - suivez l’heure du dernier message pour chaque actif ; en l’absence de mise à jour pendant > 30 s sur un marché actif, forcez une actualisation REST.
  • Collisions de nonce - py-clob-client gère les nonces d’ordres pour vous, mais si vous utilisez votre propre signataire, incrémentez le nonce à chaque ordre.
  • Solde insuffisant - vérifiez toujours le solde USDC avant de placer un ordre ; le carnet peut afficher votre ordre, mais le matching le rejettera.
  • Marché en pause ou en cours de résolution - vérifiez market.active && !market.closed avant de trader. Autour de la résolution, les mises à jour Gamma accusent quelques secondes de retard sur CLOB.
  • Incompatibilité de l’adaptateur NegRisk - les marchés à issues multiples passent par un adaptateur NegRisk distinct. Le SDK le gère, mais confirmez que votre ordre a bien été envoyé vers la bonne place.

Partie 12 : Récompenses de liquidité via API

Polymarket distribue environ 5 millions de dollars par mois en récompenses générales de liquidité, ainsi que plus de 5 millions de dollars par mois en récompenses propres au sport (voir Liquidity Rewards). La majeure partie revient à des market makers pilotés par API. Ils maintiennent des cotations serrées des deux côtés du carnet sur des milliers de marchés.

La formule de récompense privilégie les ordres proches du midpoint, en tenant compte de la taille et du temps passé dans le carnet. Voici une boucle minimale de market making :

  1. Lisez le carnet d’ordres du marché cible
  2. Calculez un midpoint équitable (par exemple, le VWAP des 3 meilleurs niveaux de chaque côté)
  3. Placez une offre d’achat à mid − spread_target/2 et une offre de vente à mid + spread_target/2
  4. À chaque mise à jour WebSocket, ajustez le prix si votre cotation s’écarte de la cible de plus d’un tick
  5. Annulez et sortez si le carnet se vide ou si une actualité tombe

Partie 13 : Passage en production

  • Hébergement : un VPS à 6 dollars par mois (Hetzner, DigitalOcean) en Europe ou sur la côte Est des États-Unis suffit pour la plupart des bots. Colocalisez-le avec Polygon RPC si vous avez besoin d’une latence inférieure à 10 ms.
  • RPC : utilisez Alchemy, Infura ou QuickNode pour un Polygon RPC fiable. Les offres gratuites conviennent tant que vous ne placez pas des centaines d’ordres par minute.
  • Surveillance : Prometheus + Grafana pour les métriques ; un bot Telegram pour les alertes. Journalisez chaque ID d’ordre que vous envoyez et chaque exécution que vous recevez.
  • Sauvegardes : persistez l’état toutes les minutes. Si le VPS tombe au milieu d’une exécution, vous devez pouvoir reprendre en quelques secondes, pas réconcilier à la main.
  • Fiscalité : votre journal sert aussi de piste d’audit ; consultez le Tax Guide.

Partie 14 - Conseils pratiques validés pour l’API Polymarket

Situation → Action : aide-mémoire

SituationActionWhy
401 « invalid api key » au premier appelVérifiez que signature_type correspond à l’origine du wallet et que funder est l’adresse proxyUne incompatibilité entre le type 1 et le type 2 explique 80 % des erreurs 401 ; le reste vient d’un EOA utilisé comme funder
Ordres rejetés avec « insufficient balance »Interrogez /balance-allowance avant chaque ordre et réservez localementCLOB réserve le collatéral dès la publication de l’ordre ; deux ordres concurrents peuvent engager les mêmes fonds deux fois
Limitation 429 sur l’endpoint /orderAppliquez un backoff avec jitter : 2^attempt + random(), plafonné à 30 sCloudflare limite le débit au lieu de rejeter les requêtes ; une relance naïve amplifie la file d’attente
WebSocket déconnecté en pleine transactionRécupérez un instantané du carnet via REST, réconciliez l’état local, puis réabonnez-vousLes deltas pendant l’interruption sont perdus ; l’instantané resynchronise les échelles de prix
Ordre placé, mais aucune confirmation d’exécutionInterrogez /data/order/{id} dans les 5 s ; s’il est pending, attendez ; s’il est introuvable, remplacez-leCas rare, mais récupérable ; par défaut, vérifiez l’état avant d’agir
Marché résolu alors qu’une cotation est activeAnnulez tous les ordres ouverts sur ce conditionId lors de l’événement de résolutionAprès la résolution, des ordres peuvent rester et produire des exécutions zombies si des particularités de l’adaptateur se manifestent
Exécution d’un bot de market-makingCotez à moins de 2 cents du midpoint avec une taille d’au moins 100 partsLa formule de récompense pondère le resserrement du spread, la taille et le temps passé dans le carnet ; un spread serré, de la taille et de la persistance l’emportent
Exécution d’un bot d’arbitrage sur marché multi-résultatsUtilisez FOK pour chaque jambe, pas GTCUne exécution partielle sur la jambe A avec une jambe B entièrement exécutée = exposition non couverte et perte immédiate
Premier bot à développerDéveloppez d’abord le scanner, puis le moteur de prix, puis le signal — jamais le signal en premierLes signaux sans état de carnet propre sont des pièges de corrélation ; commencez par faire fonctionner les flux
Bot de production planté à 3 h du matinPrévoyez un redémarrage automatique systemd, une alerte Telegram et un état persistantTout bot non supervisé finira par planter ; la seule question est de savoir s’il redémarre proprement

Et ensuite ?

Vérification rapide