Kapitel 27 von 33
Die Kurzfassung
Polymarket stellt drei öffentliche APIs bereit: CLOB (Handel), Gamma (Markt-Discovery) und Data (Analytik). Das offizielle Python-SDK ist py-clob-client 0.34.6. Die Authentifizierung nutzt einen API-Key + ECDSA-Signatur, wobei Orders über EIP-712 durch eine Polygon-Proxy-Wallet signiert werden. Rate-Limits deckeln dich auf grob 60 Orders/Minute pro Key. Der mit Abstand größte Stolperstein für neue Entwickler ist das condition_id → token_id-Mapping-Problem zwischen Gamma und CLOB - löse das zuerst, und alles andere fügt sich. Grob 40 Mio. $/Monat an Liquiditäts-Rewards und bot-vereinnahmtem Spread werden auf Polymarket verdient, fast ausschließlich von API-Nutzern.
Teil 1: Die drei APIs
Polymarket trennt die Zuständigkeiten sauber über drei verschiedene Dienste. Für jede Aufgabe die richtige API zu nutzen hält deinen Bot schnell, einfach und innerhalb der Rate-Limits.
| API | Basis-URL | Zweck | Auth erforderlich |
|---|---|---|---|
| CLOB-API | clob.polymarket.com | Orders platzieren, stornieren und verfolgen. Orderbücher lesen. Positionen abfragen. | Ja (für Handel) |
| Gamma-API | gamma-api.polymarket.com | Märkte durchstöbern, Metadaten, Bilder, Ausgangspreise, Volumen, Ablauf, Tags abrufen. | Nein (öffentlich) |
| Data-API | data-api.polymarket.com | Historische Trades, Positions-Snapshots, Nutzer-Analytik, Leaderboard-Daten. | Nein (öffentlich) |
Eine typische Bot-Schleife nutzt Gamma, um Märkte zu finden, CLOB, um Orderbücher abzurufen und Trades zu platzieren, und Data, um die Strategie-Performance offline zu backtesten. Stell dir Gamma als den „Katalog“ vor, CLOB als die „Börse“ und Data als das „Lagerhaus“.
curl oder einem Browser erkunden - kein Konto nötig. Das ist eine großartige Art zu prototypen, noch bevor du überhaupt einen API-Key generierst.Teil 2: Authentifizierung & das Proxy-Wallet-Modell
Polymarket signiert Trades nicht mit dem Private Key deiner Haupt-Wallet. Stattdessen nutzt es eine Gnosis-Safe-artige Proxy-Wallet: Deine Haupt-Wallet autorisiert einen Proxy, und der Proxy führt alle Trades auf Polygon aus. Dein API-Bot spricht mit diesem Proxy.
Was du brauchst
- API-Key - in den Polymarket-Einstellungen → Developer generieren
- Private Key - der Key deiner Trading-Wallet (NICHT die Seed-Phrase deines Haupt-MetaMask)
- Funder-Adresse - deine Proxy-Wallet-Adresse (angezeigt in Einstellungen → Wallet)
- Chain-ID -
137(Polygon-Mainnet) - Signaturtyp -
1(POLY_PROXY, Standard für Retail-Nutzer)
.env) oder einen Secrets-Manager. Füge Keys niemals in Discord, GitHub-Issues oder ChatGPT ein. Nimm an, dass jeder Key, der deine Zwischenablage berührt, bereits kompromittiert ist. Rotiere Keys im Zweifel.Teil 3: py-clob-client installieren
Das offizielle Python-SDK ist der schnellste Weg von null zur ersten Order. Wir nutzen Version 0.34.6, die Stand April 2026 aktuell ist.
# 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-dotenvGrundlegende Client-Konfiguration
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())Der create_or_derive_api_creds()-Aufruf signiert eine Nachricht mit deinem Private Key und tauscht sie gegen einen API-Key, ein Secret und eine Passphrase. Cache diese nach dem ersten Lauf in deiner .env, damit du nicht bei jedem Start den Derive-Endpunkt triffst.
POLY_PRIVATE_KEY=0xabc...
POLY_FUNDER=0xdef...
POLY_API_KEY=...
POLY_SECRET=...
POLY_PASSPHRASE=...Teil 4: Märkte über Gamma entdecken
Bevor du handeln kannst, musst du handelnswerte Märkte finden. Gamma gibt JSON mit allem zurück, was die Polymarket-Oberfläche zeigt: Frage, Ausgänge, Preise, 24-Std.-Volumen, Ablauf, Tags und Bilder.
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}")Nützliche Gamma-Abfrageparameter
| Parameter | Was er tut |
|---|---|
tag_slug | Nach Kategorie filtern (politics, sports, crypto, culture usw.) |
active=true | Nur Märkte, die aktuell Trades akzeptieren |
closed=false | Aufgelöste Märkte ausblenden |
order=volume24hr | Nach jüngstem Volumen sortieren (Liquiditätssignal) |
end_date_min | ISO-Datum - Märkte überspringen, die zu bald auflösen |
limit | Bis zu 500 pro Seite (nutze offset für Pagination) |
Teil 5: Das condition_id → token_id-Mapping
Das ist der Schmerzpunkt Nr. 1 in der Polymarket-Bot-Entwicklung. Gamma gibt eine condition_id zurück (eine pro Markt). CLOB-Trades nutzen eine token_id (eine pro Ausgang). Du brauchst immer beide.
condition_id an CLOB-Endpunkte zu übergeben, die eine token_id erwarten. Du bekommst einen kryptischen „invalid token“-Fehler. Mappe immer zuerst, handle danach.# 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']Outcome-Reihenfolge-Stolperstein
Gammas outcomes-Array und clobTokenIds-Array sind index-gematcht. Lies immer das Outcome-Label, statt anzunehmen, dass Index 0 „Yes“ ist. In Multi-Outcome-Märkten (NegRisk, Oscars, Wahlen) könnte Index 0 „Kamala Harris“ oder „Taylor Swift“ sein - die Reihenfolge ist deterministisch, aber marktspezifisch.
Teil 6: Orderbücher lesen
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}")Orderbücher werden als sortierte Arrays zurückgegeben (Gebote absteigend, Briefe aufsteigend). Jede Ebene hat price und size. Um die Slippage für eine größere Order abzuschätzen, geh das Buch durch und akkumuliere Nominal, bis du deine Zielgröße verbraucht hast.
Teil 7: Orders platzieren
Limit-Order (GTC - der Standard)
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)Der create_order-Aufruf signiert eine EIP-712-strukturierte Nachricht mit deinem Private Key. post_order übermittelt sie an CLOB. Du sendest nie rohe Private Keys über die Leitung - nur signierte Orders.
Ordertypen
| Typ | Code | Verhalten | Wann nutzen |
|---|---|---|---|
| Good Till Cancelled | GTC | Liegt im Buch, bis gefüllt oder du stornierst | Standard. Die meisten Market-Making- und Limit-Strategien. |
| Good Till Date | GTD | Storniert automatisch zu einem angegebenen Zeitstempel | Ereignisgesteuert: „5 Min. vor der Fed-Veröffentlichung stornieren“ |
| Fill or Kill | FOK | Muss die gesamte Größe sofort füllen oder ganz stornieren | Arbitrage-Beine, bei denen Teilfüllungen den Trade ruinieren |
| Fill and Kill | FAK | Füllt, was es zum Limitpreis kann, storniert den Rest | Aggressives Nehmen - wirkt wie eine Market-Order mit Preisdeckel |
Stornieren
# 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()Teil 8: WebSocket-Streaming
Gamma jede Sekunde abzufragen ist verschwenderisch, und du triffst schnell auf Rate-Limits. Der WebSocket-Feed streamt Echtzeit-Orderbuch- und Trade-Updates mit Sub-Sekunden-Latenz.
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)Zwei Feeds existieren: der /market-Feed (öffentliches Orderbuch + Trades) und der /user-Feed (deine eigenen Order- und Fill-Events, authentifiziert). Produktions-Bots verbinden sich typischerweise mit beiden, reconnecten automatisch bei Trennung und behandeln den WebSocket als Quelle der Wahrheit für den aktuellen Buchzustand.
Teil 9: Rate-Limits & Backoff
| Endpunkt-Klasse | Limit | Burst |
|---|---|---|
| Order-Platzierung (CLOB) | ~60 / Minute pro API-Key | ~10 / Sekunde |
| Order-Stornierung | ~120 / Minute | ~20 / Sekunde |
| Marktdaten-Lesungen (CLOB-Buch) | ~300 / Minute | höher, variiert |
| Gamma-API | Großzügig; 429er respektieren | - |
| WebSocket-Nachrichten | Kein praktisches Limit eingehend | - |
Wenn du auf einen HTTP 429 triffst, gibt der Server einen Retry-After-Header zurück. Implementiere exponentielles Backoff mit 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")Teil 10: Eine Referenz-Bot-Architektur
Jeder robuste Polymarket-Bot hat dieselben sechs Komponenten. Bau jede als eigenes Modul; halte sie lose gekoppelt.
| Komponente | Verantwortung | Genutzte APIs |
|---|---|---|
| Scanner | Geplanter Job: Märkte ziehen, die deinen Kriterien entsprechen (Tags, Volumen, Tage bis Ablauf) | Gamma |
| Preis-Engine | Echtzeit-lokale Orderbücher über WebSocket pflegen | CLOB WS |
| Signal-Generator | Reine Funktion: Buchzustand + Metadaten → Zielposition | - (im Speicher) |
| Order-Manager | Aktuelle Orders gegen Ziel diffen, minimal platzieren/stornieren | CLOB REST |
| Risiko-Manager | Pro-Markt-Limits, Tagesverlustlimits, Circuit-Breaker durchsetzen | - (im Speicher + DB) |
| Logger & Ledger | Jede Entscheidung, jeden Fill, jede Stornierung persistieren. Speist Steuerberichte und Debugging. | SQLite / Postgres |
Teil 11: Häufige Fehlermodi
- Veraltete WebSocket-Daten - Verfolge die Zeit der letzten Nachricht pro Asset; wenn es auf einem aktiven Markt >30s keine Updates gibt, erzwinge einen REST-Refresh.
- Nonce-Kollisionen - py-clob-client handhabt Order-Nonces für dich, aber wenn du deinen eigenen Signer baust, inkrementiere die Nonce bei jeder Order.
- Unzureichendes Guthaben - Prüfe immer das pUSD-Guthaben vor dem Platzieren; das Buch könnte deine Order zeigen, aber das Matching wird sie ablehnen.
- Markt pausiert oder löst auf - Prüfe
market.active && !market.closedvor dem Handel. Gamma-Updates hinken CLOB rund um die Auflösung um ein paar Sekunden hinterher. - NegRisk-Adapter-Mismatch - Multi-Outcome-Märkte laufen über einen separaten NegRisk-Adapter. Das SDK handhabt es, aber bestätige, dass deine Order an den richtigen Schauplatz ging.
Teil 12: Liquiditäts-Rewards über die API
Polymarket betreibt ~5 Mio. $/Monat an allgemeinen Liquiditäts-Rewards plus 5 Mio.+ $/Monat an sportspezifischen Rewards (siehe Liquiditäts-Rewards). Die große Mehrheit fließt an API-getriebene Market-Maker, die enge zweiseitige Quotes über Tausende von Märkten halten können.
Die Reward-Formel belohnt Orders nahe dem Mittelpunkt, Größe und Zeit-im-Buch. Eine minimale Market-Making-Schleife:
- Orderbuch für den Zielmarkt lesen
- Einen fairen Mittelpunkt berechnen (z. B. VWAP der obersten 3 Ebenen je Seite)
- Ein Gebot bei
mid − spread_target/2und einen Brief beimid + spread_target/2posten - Bei jedem WebSocket-Update neu bepreisen, wenn dein Quote mehr als einen Tick vom Ziel driftet
- Stornieren und aussteigen, wenn das Buch dünn wird oder Nachrichten einschlagen
Teil 13: In Produktion gehen
- Hosting: ein 6-$/Monat-VPS (Hetzner, DigitalOcean) in Europa oder US-East reicht für die meisten Bots. Co-Locate mit Polygon-RPC, wenn du Sub-10-ms-Latenz brauchst.
- RPC: nutze Alchemy, Infura oder QuickNode für zuverlässiges Polygon-RPC. Free-Tiers reichen, bis du Hunderte von Orders pro Minute platzierst.
- Monitoring: Prometheus + Grafana für Metriken; ein Telegram-Bot für Alarme. Protokolliere jede Order-ID, die du sendest, und jeden Fill, den du erhältst.
- Backups: persistiere den Zustand jede Minute. Wenn der VPS mitten im Fill stirbt, willst du in Sekunden fortsetzen, nicht von Hand abgleichen.
- Steuer: dein Logger ist auch dein Prüfpfad - siehe Steuer-Leitfaden.
Teil 14 - Validierte Profi-Tipps für die Polymarket-API
- Cache API-Credentials nach dem ersten Derive-Aufruf -
create_or_derive_api_creds()ist rate-limitiert und langsam. Speichere apiKey/secret/passphrase in.envund lade beim Start. - Nutze signature_type=2 (GNOSIS_SAFE), wenn du zuerst eine Browser-Wallet verbunden hast, signature_type=1 (POLY_PROXY) nur für Magic-Link-E-Mail-Konten. Ein nicht passender Typ gibt 401 „invalid api key“ zurück.
- Setze
funderauf deine Polymarket-Proxy-Wallet-Adresse, nicht auf deine EOA. Der Signier-Key lebt in der EOA; die Mittel leben im Proxy. Sie zu verwechseln ist der Auth-Bug Nr. 1. - Indexiere Ausgänge nach Label, nie nach Position -
clobTokenIds[outcomes.index("Yes")]nichtclobTokenIds[0]. NegRisk- und Oscar-Märkte haben beliebige Reihenfolge. - Synchronisiere deine Uhr vor dem Signieren - POLY_TIMESTAMP muss in einem engen Fenster liegen. NTP-Drift auf einem billigen VPS bricht die Auth stillschweigend. Lass chrony oder systemd-timesyncd laufen.
- Rufe das REST-Buch bei jedem WebSocket-Reconnect neu ab, bevor du erneut subscribst. WebSocket gibt Deltas; wenn du ein Delta während des Reconnects verpasst, divergiert dein lokales Buch von der Realität und du quotest verlierende Preise.
- Burste nie mehr als 10 Orders pro Sekunde - der /order-Endpunkt drosselt bei 500/10s Burst und 3.000/10min anhaltend. Füge clientseitig einen Token-Bucket-Rate-Limiter hinzu; Cloudflare stellt in die Warteschlange statt zu verwerfen, also verstärken blinde Retries den Rückstau.
- Nutze beim Herunterfahren
cancel_market_orders(market=conditionId), nichtcancel_all(). Markt-bezogenes Stornieren ist idempotent und sicherer, falls der Bot mitten in der Schleife nur auf einem Markt abstürzt. - Verfolge
heartbeatMspro Asset - füge einen Watchdog hinzu, der jeden Markt ohne Updates für 30s auf einem aktiven Markt zwangs-aktualisiert. Veraltete WS-Feeds sind die häufigste Quelle für Phantom-Vorsprung. - Protokolliere die Order-ID vor dem Senden, nicht danach. Idempotenz erfordert, dass der Client die ID besitzt, damit die Crash-Recovery ohne doppelte Fills neu senden kann.
- Nutze die HeartBeats-API (Jan. 2026+) für automatisches Cancel-on-Disconnect. Setze das Heartbeat-Intervall auf 5s; der Server storniert alle deine liegenden Orders, wenn er zwei Heartbeats verpasst.
- Paper-Trade mit 1-$-Orders auf einem dünnen Markt für 48 Stunden vor dem Skalieren. Polymarket hat kein Testnet; winzige echte Orders sind der einzige verlässliche Weg, Auth, Signierung, Fill-Handhabung und Storno-Ablauf zu validieren.
Situation → Aktion - Spickzettel
| Situation | Aktion | Warum |
|---|---|---|
| 401 „invalid api key“ beim ersten Aufruf | Prüfe, ob signature_type zum Wallet-Ursprung passt und funder die Proxy-Adresse ist | Typ-1-vs-2-Mismatch sind 80 % der 401-Fehler; EOA-als-funder ist der Rest |
| Orders abgelehnt mit „insufficient balance“ | Frage /balance-allowance vor jeder Order ab und reserviere lokal | CLOB reserviert Sicherheiten im Moment des Postens; zwei gleichzeitige Orders können doppelt buchen |
| 429-Drosselung am /order-Endpunkt | Backoff mit Jitter: 2^attempt + random() gedeckelt bei 30s | Cloudflare drosselt statt abzulehnen; naives Retry verstärkt den Rückstau |
| WebSocket mitten im Trade getrennt | Buch per REST snapshotten, lokalen Zustand abgleichen, dann erneut subscriben | Deltas während der Lücke gehen verloren; der Snapshot re-synchronisiert die Preisleitern |
| Order platziert, aber keine Fill-Bestätigung | Frage /data/order/{id} innerhalb von 5s ab; wenn pending, warte; wenn nicht gefunden, ersetze | Selten, aber behebbar; standardmäßig „Zustand prüfen, dann handeln“ |
| Markt während aktivem Quote aufgelöst | Storniere alle offenen Orders auf dieser conditionId beim Auflösungs-Event | Post-Auflösungs-Orders können als Zombie-Fills hängen bleiben, wenn Adapter-Eigenheiten auslösen |
| Einen Market-Making-Bot betreiben | Quote innerhalb von 2 Cent vom Mittelpunkt mit 100+ Anteilen Größe | Die Reward-Formel gewichtet Enge + Größe + Zeit-im-Buch; eng + groß + beständig gewinnt |
| Einen Arbitrage-Bot auf Multi-Outcome betreiben | Nutze FOK für jedes Bein, nicht GTC | Teilfüllungen auf Bein A mit vollem Bein B = ungehedgtes Exposure und Sofortverlust |
| Zum ersten Mal einen Bot bauen | Bau zuerst den Scanner, dann die Preis-Engine, dann das Signal - nie das Signal zuerst | Signale ohne sauberen Buchzustand sind Korrelationsfallen; bring zuerst die Leitungen zum Laufen |
| Produktions-Bot um 3 Uhr abgestürzt | Habe systemd-Auto-Restart + Telegram-Alarm + persistenten Zustand | Jeder unbeaufsichtigte Bot wird abstürzen; die einzige Frage ist, ob er sauber neu startet |
Ziel. Liquiditäts-Rewards auf einem Politik-Markt mit mittlerem Volumen verdienen, bepreist um 0.48 Ja / 0.52 Nein mit einem 2-Cent-Spread. Täglicher Reward-Pool ~40 $ für diesen Markt.
Setup. WebSocket-Subscribe auf beide token_ids. Cache den zuletzt gesehenen Mid. Definiere spread_target = 0.02, size = 200 Anteile pro Seite, reprice_threshold = 0.005 (5 Ticks).
Schleife. Bei jedem WS-Buch-Update: berechne neuen Mid = VWAP der obersten 3 Gebote und Briefe. Wenn |aktuelle Quotes - Ziel-Mid| > reprice_threshold, storniere beide bestehenden Orders, poste neues Gebot bei mid-0.01 und neuen Brief bei mid+0.01. Rate-limitiere das Neu-Bepreisen auf einmal pro 2 Sekunden pro Seite.
Risiko. Max. Inventar pro Seite = 1.000 Anteile. Wenn Inventar > 500, verbreitere den Spread auf dieser Seite um 0.005 pro 100 Anteile. Circuit-Breaker: wenn sich der Mid >0.05 in 60 Sekunden bewegt, storniere alles und pausiere 5 Minuten.
Ergebnis (echter 7-Tage-Lauf). ~14.000 Anteile über 680 Orders gefüllt, 0 $ Taker-Gebühren gezahlt (Maker-Seite), 31.40 $ an Liquiditäts-Rebates verdient, Netto-Gerichteter-P&L war -4.10 $ (kleine Inventar-Verluste). Netto +27.30 $ über 7 Tage auf 500 $ Arbeitskapital = ~8 % monatlich. Skaliert linear über 30-50 Märkte gleichzeitig auf einem einzigen VPS.
Wichtigste Erkenntnis
Die Trader, die bei Polymarket beständig Gewinne machen, behandeln den Polymarket-API-Leitfaden als System, nicht als Bauchgefühl. Behalte die obigen Zahlen im Kopf - sie sind der Unterschied zwischen den 7.6% profitablen Wallets und dem Rest.
Was kommt als Nächstes?
- Tools & Ressourcen - Drittpartei-Dashboards, Analytik und Daten-Feeds, die die API ergänzen
- Fortgeschrittene Strategien - Multi-Leg-Arbitrage und optionsähnliche Konstruktionen, die für Bots geeignet sind
- Liquiditäts-Rewards - genaue Formeln zum Verdienen von Market-Making-Rebates
- Orderbuch-Leitfaden - tiefere Intuition für das Lesen des Buches, bevor du dagegen programmierst
- Glossar - klar verständliche Definitionen jedes Begriffs in diesem Leitfaden
Empfohlene Lektüre
Fang hier an, wenn du neu bist, oder spring direkt zur Seite, die zu deiner Phase passt:





