API- und Webhook-Integration: Predite in den eigenen Stack einbinden
Der Großteil von Predite spielt sich im Dashboard ab, doch die spannendsten Sonderfälle entstehen, sobald man aufhört zu klicken und anfängt zu skripten. Vielleicht möchtest du EV-Signale in einen Discord-Kanal leiten, sobald der Scanner sie findet. Vielleicht betreibst du einen Bot außerhalb der Plattform und brauchst deine Live-Positionen auf Polymarket jede Minute als JSON. Vielleicht soll ein Stop-Loss-Trigger dich anpiepsen, in Slack posten und einen n8n-Workflow auslösen, der die Position automatisch schließt. Genau dafür gibt es die Predite-API und das Webhook-System.
Dieser Leitfaden führt dich durch die gesamte Integrationsfläche von Anfang bis Ende: einen API-Key erzeugen, die REST-Endpunkte mit Bearer-Authentifizierung aufrufen, Rate Limits einhalten, Webhook-Events abonnieren, Zustellungssignaturen mit HMAC-SHA256 verifizieren und schließlich alles zu einer eigenen Alerting-Pipeline zusammenfügen. Alles hier ist nur im Bot-Plan verfügbar ($99/Monat) — die Pläne Starter ($29) und Pro ($59) nutzen das Dashboard und die integrierten Benachrichtigungen, doch der programmatische Zugriff (API-Keys und ausgehende Webhooks) ist dem Bot-Plan vorbehalten.
Einen API-Key erzeugen
Predite verwendet langlebige API-Keys für die REST-API. Sie sind an dein Konto gebunden, sodass jeder Key dein Portfolio und den Signal-Feed lesen kann und sonst nichts — es gibt keine Schreib-Endpunkte, ein durchgesickerter Key kann also weder Trades platzieren noch Gelder bewegen.
- Öffne Einstellungen → API im Dashboard. Wenn du nicht im Bot-Plan bist, siehst du statt des Key-Managers eine Upgrade-Aufforderung.
- Klicke auf API-Key erstellen und gib ihm ein aussagekräftiges Label, etwa
n8n-prododerdiscord-bot. Das Label dient nur deiner eigenen Übersicht, wenn du mehrere Keys hast. - Predite erzeugt den Key und zeigt ihn dir genau einmal an. Kopiere ihn sofort und lege ihn in einem Secrets-Manager oder deiner Umgebung ab.
Ein Key sieht so aus:
``
pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Das Format besteht aus dem Präfix pdt_live_ gefolgt von 32 Hex-Zeichen — insgesamt 40 Zeichen. Auf dem Server wird nur ein SHA-256-Hash des vollständigen Keys gespeichert, dazu die ersten 16 Zeichen (pdt_live_ + 7 Zeichen) als nicht geheimes Präfix, damit das Dashboard dir in der Key-Liste pdt_live_a1b2c3… anzeigen kann, ohne das Geheimnis jemals im Klartext zu speichern. Das ist auch der Grund, warum wir dir den vollständigen Key nach der Erstellung nicht erneut zeigen können: Wir haben ihn schlicht nicht.
Ein paar betriebliche Hinweise:
- •Das Widerrufen eines Keys ist ein Soft Delete. Der Datensatz bleibt zu Audit-Zwecken erhalten, der Status wechselt auf
revoked, und jede Anfrage, die ihn verwendet, gibt sofort 401 zurück. Rotiere, indem du einen neuen Key erstellst, ihn ausrollst und dann den alten widerrufst. - •Das Dashboard erfasst
last_used_atund einen Anfragezähler pro Key, sodass du einen Key erkennst, der verstummt ist (ein Zeichen, dass deine Integration ausgefallen ist), oder einen, der stärker beansprucht wird als erwartet (ein Zeichen, dass er durchgesickert ist). - •Behandle den Key wie ein Passwort. Committe ihn niemals, packe ihn nie in clientseitiges JavaScript, füge ihn nie in einen Screenshot ein. Aus diesem Grund lesen alle Beispiele weiter unten ihn aus einer Umgebungsvariablen.
Anfragen authentifizieren
Jeder Aufruf der REST-API trägt den Key im Standard-Header Authorization als Bearer-Token:
``
Authorization: Bearer pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Der Server extrahiert das Token, hasht es, sucht den passenden aktiven Key und vergleicht die Hashes. Fehlt der Header, ist er fehlerhaft, verweist auf einen widerrufenen Key oder ist der Key abgelaufen, erhältst du:
``json
{ "error": "invalid_api_key" }
``
mit HTTP-Status 401. Es gibt keinen separaten Login-Schritt und kein Token-Refresh — der Key ist die Berechtigung. Setze ihn einmal in deinem Client, und schon bist du fertig.
Die REST-Endpunkte
Die v1-API ist bewusst klein und ausschließlich lesend. Zwei Endpunkte decken die Daten ab, die die meisten Integrationen tatsächlich brauchen: dein Portfolio und den Signal-Feed. Beide liegen unter /api/v1/ und liefern beide JSON mit einem generated_at-ISO-Zeitstempel, damit du erkennst, wie aktuell eine Antwort ist.
GET /api/v1/portfolio
Gibt deine Live-Positionen und eine Portfolio-Zusammenfassung zurück, gelesen aus dem Polymarket-Wallet, das du unter Einstellungen → Plattformen verbunden hast.
``bash
curl -s https://app.predite.io/api/v1/portfolio \
-H "Authorization: Bearer $PREDITE_API_KEY"
``
Eine typische Antwort:
``json
{
"wallet": "0x3a1f...8c2d",
"positions": [
{
"market": "Will the Fed cut rates in July 2026?",
"outcome": "Yes",
"shares": 420.0,
"avg_price": 0.61,
"current_price": 0.68,
"value": 285.6,
"unrealized_pnl": 29.4
}
],
"summary": {
"total_value": 4128.55,
"unrealized_pnl": 312.10,
"open_positions": 7
},
"generated_at": "2026-06-01T14:32:08.114Z"
}
``
Wenn du noch kein Wallet verbunden hast, erhältst du { "positions": [], "summary": null, "error": "no_wallet_connected" } mit Status 200 — prüfe also das Feld error, statt anzunehmen, dass eine 200 Daten bedeutet. Der Endpunkt liest dieselben On-Chain-Positionsdaten, die auch das Dashboard nutzt, sodass das P&L hier mit dem übereinstimmt, was du in der UI siehst.
GET /api/v1/signals
Gibt die besten EV-Signale (Expected Value) aus dem Markt-Scanner zurück — dieselbe Engine, die den Signal-Feed des Dashboards speist. Jedes Signal vergleicht den aktuellen Marktpreis mit Predites KI-geschätztem Fair Price und gibt den Edge in Prozentpunkten an.
Er akzeptiert zwei Query-Parameter:
- •
limit— wie viele Signale zurückgegeben werden, 1 bis 100, Standard 20. - •
min_edge— minimaler Edge in Prozentpunkten, Standard 5. Damit filterst du marginale Signale heraus.
``bash
curl -s "https://app.predite.io/api/v1/signals?limit=10&min_edge=8" \
-H "Authorization: Bearer $PREDITE_API_KEY"
``
``json
{
"signals": [
{
"market_title": "Will Bitcoin close above $120k on 2026-06-30?",
"direction": "YES",
"edge_pp": 11.4,
"market_price": 0.42,
"ai_price": 0.534,
"created_at": "2026-06-01T14:05:00.000Z"
}
],
"count": 1,
"filters": { "min_edge": 8, "limit": 10 },
"generated_at": "2026-06-01T14:32:40.781Z"
}
``
Hier bedeutet ein edge_pp von 11.4, dass der KI-Fair-Price (0.534) auf der YES-Seite 11.4 Punkte über dem Marktpreis (0.42) liegt — genau die Art von Lücke, nach der ein EV-Trader sucht. Die Ergebnisse sind absteigend nach Edge sortiert, danach nach Aktualität. Der filters-Block gibt zurück, was angewendet wurde, sodass du bestätigen kannst, dass deine Parameter korrekt geparst wurden.
Ein Python-Client
Hier ist ein kleiner Wrapper, der die Authentifizierung und beide Endpunkte abwickelt. Er nutzt requests, doch die Struktur lässt sich auf jeden HTTP-Client übertragen.
```python import os import requests
BASE = "https://app.predite.io/api/v1" KEY = os.environ["PREDITE_API_KEY"] SESSION = requests.Session() SESSION.headers["Authorization"] = f"Bearer {KEY}"
def get_portfolio(): r = SESSION.get(f"{BASE}/portfolio", timeout=10) r.raise_for_status() return r.json()
def get_signals(limit=20, min_edge=5.0): params = {"limit": limit, "min_edge": min_edge} r = SESSION.get(f"{BASE}/signals", params=params, timeout=10) r.raise_for_status() return r.json()
if __name__ == "__main__": for s in get_signals(limit=5, min_edge=10)["signals"]: print(f"{s['edge_pp']:+.1f}pp {s['direction']:>3} {s['market_title']}") ```
Die Wiederverwendung einer einzigen Session hält die Verbindung über Aufrufe hinweg am Leben, was wichtig wird, sobald du nach einem festen Zeitplan pollst.
Rate Limits
Die API erlaubt 120 Anfragen pro Minute pro Key, und diese Obergrenze ist in jedem Plan gleich. Für die vorgesehene Nur-Lese-Last ist das großzügig: Das Portfolio einmal pro Minute abzufragen verbraucht weniger als 1 % davon und lässt reichlich Luft für Signalabfragen. Wer darüber liegt, bekommt 429 samt Obergrenze im Body zurück — dann zurückfahren statt sofort erneut zu versuchen.
Ein paar Gewohnheiten halten dich gut innerhalb des Limits:
- •Polle nicht schneller als nötig. EV-Signale werden im Cron-Takt des Scanners aktualisiert, nicht kontinuierlich, sodass ein Poll von
/signalsalle paar Sekunden nur Quota verbrennt, um identische Daten zurückzugeben. Einmal pro Minute reicht völlig; einmal alle paar Minuten ist oft genug. - •Bevorzuge Webhooks für Events. Wenn du pollst, um zu erkennen, *dass etwas passiert ist* — ein Whale hat sich bewegt, ein Stop-Loss hat ausgelöst —, nutzt du das falsche Werkzeug. Webhooks (siehe unten) schieben dir diese Ereignisse in dem Moment zu, in dem sie auftreten, ganz ohne Polling-Kosten.
- •Cache innerhalb eines Ticks. Wenn drei Teile deiner Pipeline das Portfolio brauchen, hole es einmal und reiche es weiter, statt es dreimal abzurufen.
- •Drossle bei Fehlern. Solltest du je gedrosselt werden, nimm es als Signal, langsamer zu werden, nicht als Anlass, in einer engen Schleife erneut zu versuchen.
Das Denkmodell: Nutze die REST-API, um Zustand bei Bedarf abzurufen, und nutze Webhooks, um auf Events zu reagieren. Diese beiden zu verwechseln ist die häufigste Ursache für verschwendete Quota.
Webhook-Abonnements
Webhooks kehren die Richtung um. Statt dass du Predite fragst „Gibt es was Neues?“, ruft Predite *deine* URL in dem Moment auf, in dem etwas passiert. Da die Payload schlicht JSON über HTTPS-POST ist, eignet sich alles als Ziel, das eine HTTP-Anfrage empfangen kann: eingehende Webhooks von Discord und Slack, Automatisierungsplattformen wie n8n und Zapier oder dein eigener Server.
Ein Abonnement erstellen
- Gehe zu Einstellungen → Webhooks (Bot-Plan erforderlich).
- Klicke auf Webhook hinzufügen und füge deine Ziel-URL ein. In der Produktion muss diese
https://sein — reines HTTP wird abgelehnt. - Gib ihm ein Label und wähle aus, welche Event-Typen es empfangen soll. Ein Abonnement muss auf mindestens ein Event lauschen.
- Speichern. Predite erzeugt ein Signing-Secret und zeigt es in der Detailansicht des Abonnements an.
Das Secret sieht so aus:
``
whsec_4f8c...d2a1
``
(das Präfix whsec_ plus 64 Hex-Zeichen). Du brauchst es, um Zustellungen zu verifizieren — lege es zusammen mit dem Ziel ab, idealerweise als Umgebungsvariable auf dem empfangenden Dienst.
Event-Typen
Sechs Event-Typen sind abonnierbar, und alle sechs werden heute zugestellt:
- •`ev_signal` — der Scanner hat eine neue EV-Gelegenheit über der Schwelle gefunden.
- •`arb_opportunity` — zwischen Polymarket und Kalshi ist auf gleichwertigen Märkten eine Arbitragelücke aufgetaucht.
- •`stop_loss_triggered` — eine deiner Absicherungen hat ihre Schwelle überschritten und ausgelöst.
- •`bot_trade_executed` — einer deiner Bots hat einen Trade eröffnet oder geschlossen.
- •`whale_move` — eine verfolgte Wallet hat eine Position ab 25.000 $ eingenommen. Globales Ereignis: Jeder Abonnent bekommt es, und jede Bewegung wird dedupliziert, damit sie nie zweimal ankommt.
- •`resolution_imminent` — ein Markt, *den du hältst*, ist in den letzten 24 Stunden vor der Auflösung. Pro Nutzer, und einmal pro Markt gesendet.
Die letzten beiden waren abonnierbar, aber stumm: Ihre Quellen sind reine Abfragen, ohne Gedächtnis über bereits Gesendetes würde ein Cron dieselben Ereignisse bei jedem Lauf erneut auslösen. Dieses Gedächtnis gibt es jetzt — deshalb sind sie zustellbar.
Abonniere jedes Ziel nur für die Events, die es interessieren. Dein On-Call-Pager will wahrscheinlich nur stop_loss_triggered; ein Research-Kanal vielleicht whale_move und ev_signal; ein Automatisierungsablauf, der rebalanciert, vielleicht bot_trade_executed.
Zustellungs-Payload und Header
Jede Zustellung ist ein HTTP-POST mit einem JSON-Body in dieser Form:
``json
{
"event": "ev_signal",
"timestamp": "2026-06-01T14:33:12.004Z",
"data": {
"market_title": "Will the ECB hold rates in June 2026?",
"direction": "NO",
"edge_pp": 9.2,
"market_price": 0.71,
"ai_price": 0.618
}
}
``
Das Feld event nennt dir den Typ, timestamp ist der Zeitpunkt, zu dem Predite es versendet hat, und data ist die ereignisspezifische Payload. Neben dem Body sendet Predite diese Header:
- •
X-Predite-Event— der Event-Typ, sodass du routen kannst, ohne den Body zu parsen. - •
X-Predite-Timestamp— der Versandzeitpunkt (stimmt mit demtimestampim Body überein). - •
X-Predite-Signature— die HMAC-SHA256-Signatur, formatiert alssha256=<hex>. - •
User-Agent: Predite-Webhooks/1.0.
Dein Endpunkt sollte schnell mit einem beliebigen 2xx-Status antworten. Die Zustellung läuft nach 5 Sekunden ab, bestätige also zügig und erledige aufwendige Arbeit asynchron — gib 200 zurück und verarbeite dann.
Zuverlässigkeitsverhalten
Auf Predites Seite ist die Zustellung nicht „fire-and-forget“. Fehlgeschlagene Zustellungen (eine Nicht-2xx-Antwort, ein Timeout oder ein Netzwerkfehler) werden protokolliert, und vorübergehende 5xx-/Netzwerkfehler werden mit exponentiellem Backoff wiederholt. Häuft ein Abonnement 10 aufeinanderfolgende Fehlschläge an, deaktiviert Predite es automatisch (Status disabled_by_failures), um nicht weiter auf einen toten Endpunkt einzuhämmern. Den Fehlergrund siehst du unter Einstellungen → Webhooks, und das erneute Aktivieren des Abonnements setzt den Fehlerzähler zurück. Jedes Abonnement erfasst außerdem den letzten Erfolg, den letzten Fehlschlag und die Gesamtzahl der Zustellungen, sodass du den Zustand auf einen Blick prüfen kannst.
Webhook-Signaturen verifizieren
Da deine Webhook-URL nur ein HTTP-Endpunkt ist, könnte jeder, der sie erfährt, gefälschte Events posten. Der Signatur-Header ist das Mittel, mit dem du beweist, dass eine Zustellung wirklich von Predite stammt und unterwegs nicht manipuliert wurde.
Das Schema ist unkompliziert: Predite berechnet HMAC-SHA256(raw_request_body, your_secret) und sendet den Hex-Digest in X-Predite-Signature als sha256=<digest>. Auf deiner Seite berechnest du denselben HMAC über den rohen, ungeparsten Body neu und vergleichst. Der Teil „Roh-Body“ ist entscheidend — wenn dein Framework JSON parst und du es erneut serialisierst, können sich Whitespace und Schlüsselreihenfolge unterscheiden und die Signatur stimmt nicht überein. Erfasse die Bytes vor dem Parsen.
Ein timing-sicherer Vergleich in Python:
```python import hashlib import hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool: if not signature_header or not signature_header.startswith("sha256="): return False sent = signature_header.split("=", 1)[1] expected = hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(sent, expected) ```
Ein minimaler Flask-Empfänger, der alles Unsignierte ablehnt:
```python import os from flask import Flask, request, abort
app = Flask(__name__) SECRET = os.environ["PREDITE_WEBHOOK_SECRET"]
@app.post("/predite-webhook") def receiver(): raw = request.get_data() # raw bytes, before JSON parsing if not verify(raw, request.headers.get("X-Predite-Signature", ""), SECRET): abort(401) payload = request.get_json() event = payload["event"] # route on event, then return fast handle(event, payload["data"]) return "", 200 ```
Verwende stets einen Vergleich mit konstanter Laufzeit (hmac.compare_digest, nicht ==), damit du keine Signatur-Bytes über das Timing preisgibst. Und lehne die Anfrage ab, bevor du echte Arbeit verrichtest, falls die Verifizierung fehlschlägt — diese eine Prüfung macht den Unterschied zwischen einer vertrauenswürdigen Pipeline und einer offenen Tür aus.
Eine eigene Alerting-Pipeline bauen
Fügen wir es mit einem konkreten Beispiel zusammen: Du möchtest, dass jedes EV-Signal mit hoher Überzeugung und jeder Stop-Loss-Trigger in Slack landet, und du möchtest, dass Stop-Losses zusätzlich einen n8n-Workflow auslösen, der das Ereignis aufzeichnet und (optional) die Position über deine eigene Broker-Integration schließt.
- Erstelle zwei Webhook-Abonnements unter Einstellungen → Webhooks. Eines verweist auf deine eingehende Slack-Webhook-URL und abonniert
ev_signal. Das andere verweist auf deinen n8n-Webhook-Node und abonniertstop_loss_triggered. Jedes erhält sein eigenes Secret. - Für Slack ist der sauberste Ansatz ein winziges Relay (eine Serverless-Funktion eignet sich gut): Es verifiziert die Predite-Signatur, formatiert die
datazu einer Slack-Nachricht und postet an Slacks URL. Das Relay erlaubt dir zu filtern — etwa nur Signale weiterzuleiten, bei denendata.edge_pp >= 10gilt —, damit du im Kanal nicht zu viel Lärm machst. Märkte mit kleinem Edge werden verworfen; nur die starken alarmieren das Team. - Für n8n setze zuerst einen Webhook-Trigger-Node, dann einen Function-Node, der dieselbe HMAC-Prüfung gegen den Roh-Body ausführt, bevor irgendetwas nachgelagert läuft. Verzweige nach der Verifizierung anhand von
data, um das Ereignis zu protokollieren und deinen Broker aufzurufen. Zapier folgt demselben Muster mit einem Code-Schritt, der die Verifizierung übernimmt. - Ziehe die REST-API für Kontext hinzu. Wenn ein
stop_loss_triggered-Event eintrifft, kann dein n8n-Ablauf sofortGET /api/v1/portfoliomit deinem API-Key aufrufen, um die vollständige aktuelle Positionsmenge zu ziehen, sodass der Alert mit Live-P&L angereichert wird statt nur mit den Trigger-Daten. Das ist das Read-on-Demand-Muster, das neben dem Push-Muster arbeitet. - Mache es idempotent. Netzwerke wiederholen; dieselbe Zustellung kann zweimal eintreffen. Verschlüssele deine nachgelagerten Aktionen anhand von etwas Stabilem (dem Event plus seinem Zeitstempel oder einer ID innerhalb von
data), damit eine doppelte Zustellung nicht doppelt agiert. - Überwache den Wächter. Prüfe Einstellungen → Webhooks regelmäßig auf
disabled_by_failures, und lass dein Relay jede abgelehnte Signatur protokollieren — ein plötzlicher Anstieg bedeutet entweder, dass dein Secret rotiert wurde, oder dass jemand deinen Endpunkt abtastet.
Mit all dem hast du eine geschlossene Schleife: Predites Scanner und Risiko-Engine schieben Events in dem Moment, in dem sie auslösen, deine Relays verifizieren und filtern sie, und deine REST-Aufrufe ergänzen den Live-Zustand bei Bedarf — alles ohne eine einzige Polling-Schleife, die Quota verbrennt.
Wenn du im Bot-Plan bist, ist der schnellste Weg, das einrasten zu spüren, zu Einstellungen → Webhooks zu gehen, ein Test-Abonnement auf einen Wegwerf-Discord-Kanal zu richten, es für ev_signal zu abonnieren und das erste echte Signal als JSON-POST eintreffen zu sehen — dann verdrahtest du das Verifizierungs-Snippet von oben und bist nur noch ein Relay von der Produktion entfernt.