Integrazione API e Webhook: collegare Predite al tuo stack
La maggior parte di Predite vive nella dashboard, ma i casi limite più interessanti emergono quando smetti di cliccare e inizi a scrivere codice. Magari vuoi che i segnali EV vengano inoltrati a un canale Discord nel momento esatto in cui lo scanner li individua. Magari gestisci un bot fuori dalla piattaforma e ti servono le tue posizioni Polymarket live in formato JSON ogni minuto. Magari vuoi che un trigger di stop-loss ti avvisi, pubblichi su Slack e avvii un workflow n8n che chiude automaticamente la posizione. È esattamente per questo che esistono l'API e il sistema di webhook di Predite.
Questa guida illustra l'intera superficie di integrazione dall'inizio alla fine: generare una chiave API, chiamare gli endpoint REST con autenticazione Bearer, rispettare i rate limit, iscriversi agli eventi webhook, verificare le firme di consegna con HMAC-SHA256 e, infine, comporre il tutto in una pipeline di alerting personalizzata. Tutto ciò che trovi qui è disponibile solo con il piano Bot ($99/mese) — i piani Starter ($29) e Pro ($59) usano la dashboard e le notifiche integrate, ma l'accesso programmatico (chiavi API e webhook in uscita) è riservato al piano Bot.
Generare una chiave API
Predite usa chiavi API a lunga durata per l'API REST. Sono limitate al tuo account, quindi qualsiasi chiave può leggere il tuo portafoglio e il feed dei segnali, e nient'altro — non esistono endpoint di scrittura, quindi una chiave compromessa non può piazzare operazioni né spostare fondi.
- Apri Impostazioni → API nella dashboard. Se non sei sul piano Bot, vedrai un invito all'upgrade al posto del gestore delle chiavi.
- Clicca su Crea chiave API e assegnale un'etichetta descrittiva, come
n8n-prododiscord-bot. L'etichetta serve solo per la tua organizzazione interna quando hai più chiavi. - Predite genera la chiave e te la mostra una sola volta. Copiala subito e conservala in un secrets manager o nel tuo ambiente.
Una chiave si presenta così:
``
pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Il formato è il prefisso pdt_live_ seguito da 32 caratteri esadecimali — 40 caratteri in totale. Sul server viene memorizzato solo un hash SHA-256 della chiave completa, più i primi 16 caratteri (pdt_live_ + 7 caratteri) come prefisso non segreto, così che la dashboard possa mostrarti pdt_live_a1b2c3… nell'elenco delle chiavi senza mai conservare il segreto in chiaro. È anche per questo che non possiamo mostrarti di nuovo la chiave completa dopo la creazione: letteralmente non la abbiamo.
Alcune note operative:
- •Revocare una chiave è un soft delete. La riga viene mantenuta per l'audit, lo stato passa a
revokede qualsiasi richiesta che la usa inizia immediatamente a restituire 401. Effettua la rotazione creando una nuova chiave, distribuendola e poi revocando quella vecchia. - •La dashboard tiene traccia di
last_used_ate di un contatore di richieste per ogni chiave, così puoi individuare una chiave che è diventata silenziosa (segno che la tua integrazione si è rotta) oppure una più attiva del previsto (segno che è stata compromessa). - •Tratta la chiave come una password. Non committarla mai, non inserirla mai in JavaScript lato client, non incollarla mai in uno screenshot. Tutti gli esempi qui sotto la leggono da una variabile d'ambiente proprio per questo motivo.
Autenticare le richieste
Ogni chiamata all'API REST trasporta la chiave nell'header standard Authorization come Bearer token:
``
Authorization: Bearer pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
``
Il server estrae il token, ne calcola l'hash, cerca la chiave attiva corrispondente e confronta gli hash. Se l'header manca, è malformato, punta a una chiave revocata oppure la chiave è scaduta, ottieni:
``json
{ "error": "invalid_api_key" }
``
con stato HTTP 401. Non esiste un passaggio di login separato né un refresh del token — la chiave è la credenziale. Impostala una volta nel tuo client e hai finito.
Gli endpoint REST
L'API v1 è volutamente piccola e di sola lettura. Due endpoint coprono i dati di cui la maggior parte delle integrazioni ha realmente bisogno: il tuo portafoglio e il feed dei segnali. Entrambi si trovano sotto /api/v1/ ed entrambi restituiscono JSON con un timestamp ISO generated_at, così puoi capire quanto è recente una risposta.
GET /api/v1/portfolio
Restituisce le tue posizioni live e un riepilogo del portafoglio, letti dal wallet Polymarket che hai collegato in Impostazioni → Piattaforme.
``bash
curl -s https://app.predite.io/api/v1/portfolio \
-H "Authorization: Bearer $PREDITE_API_KEY"
``
Una risposta tipica:
``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"
}
``
Se non hai ancora collegato un wallet, otterrai { "positions": [], "summary": null, "error": "no_wallet_connected" } con stato 200 — quindi controlla la presenza del campo error invece di dare per scontato che un 200 significhi dati. L'endpoint legge gli stessi dati di posizione on-chain che usa la dashboard, quindi il P&L qui corrisponde a quello che vedi nell'interfaccia.
GET /api/v1/signals
Restituisce i principali segnali EV (expected value) dallo scanner di mercato — lo stesso motore che alimenta il feed dei segnali della dashboard. Ogni segnale confronta il prezzo di mercato attuale con il fair price stimato dall'AI di Predite e riporta l'edge in punti percentuali.
Accetta due parametri di query:
- •
limit— quanti segnali restituire, da 1 a 100, default 20. - •
min_edge— edge minimo in punti percentuali, default 5. Usalo per filtrare i segnali marginali.
``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"
}
``
Qui un edge_pp di 11.4 significa che il fair price dell'AI (0.534) si colloca 11,4 punti sopra il prezzo di mercato (0.42) sul lato YES — il tipo di divario che un trader EV cerca. I risultati sono ordinati per edge decrescente, poi per recenza. Il blocco filters restituisce ciò che è stato applicato, così puoi confermare che i tuoi parametri siano stati interpretati correttamente.
Un client Python
Ecco un piccolo wrapper che gestisce l'autenticazione ed entrambi gli endpoint. Usa requests, ma la struttura si traduce in qualsiasi client HTTP.
```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']}") ```
Riutilizzare un'unica Session mantiene la connessione attiva tra le chiamate, cosa che conta quando esegui il polling secondo una pianificazione.
Rate limit
L'API consente 120 richieste al minuto per chiave, e quel tetto è lo stesso in tutti i piani. È generoso per il carico di sola lettura per cui è pensata: interrogare il portafoglio una volta al minuto ne usa meno dell'1%, lasciando margine abbondante per controllare i segnali. Se lo superi, la chiamata torna 429 con il tetto nel corpo: rallenta invece di riprovare subito.
Alcune buone abitudini ti tengono ben dentro il limite:
- •Non fare polling più velocemente del necessario. I segnali EV si aggiornano alla cadenza del cron dello scanner, non in modo continuo, quindi fare polling di
/signalsogni pochi secondi non fa che bruciare quota restituendo dati identici. Una volta al minuto è più che sufficiente; una volta ogni pochi minuti è spesso abbastanza. - •Preferisci i webhook per gli eventi. Se stai facendo polling per rilevare *che qualcosa è accaduto* — una whale si è mossa, uno stop-loss è scattato — stai usando lo strumento sbagliato. I webhook (più sotto) te li inviano nell'istante in cui si verificano, con zero costo di polling.
- •Metti in cache all'interno di un tick. Se tre parti della tua pipeline hanno bisogno del portafoglio, recuperalo una volta e passalo in giro invece di chiamarlo tre volte.
- •Applica il backoff in caso di errori. Se vieni mai limitato, trattalo come un segnale per rallentare, non per riprovare in un loop serrato.
Il modello mentale: usa l'API REST per estrarre lo stato su richiesta e usa i webhook per reagire agli eventi. Confondere i due è la causa più comune di quota sprecata.
Iscrizioni ai webhook
I webhook invertono la direzione. Invece di essere tu a chiedere a Predite "c'è qualcosa di nuovo?", è Predite a chiamare il *tuo* URL nel momento in cui qualcosa accade. Poiché il payload è semplice JSON inviato tramite POST HTTPS, qualsiasi cosa in grado di ricevere una richiesta HTTP funziona come destinazione: i webhook in entrata di Discord e Slack, piattaforme di automazione come n8n e Zapier, oppure il tuo server.
Creare un'iscrizione
- Vai su Impostazioni → Webhook (è richiesto il piano Bot).
- Clicca su Aggiungi webhook e incolla l'URL di destinazione. In produzione deve essere
https://— l'HTTP semplice viene rifiutato. - Assegnagli un'etichetta e seleziona quali tipi di evento deve ricevere. Un'iscrizione deve ascoltare almeno un evento.
- Salva. Predite genera un segreto di firma e lo mostra nella vista di dettaglio dell'iscrizione.
Il segreto si presenta così:
``
whsec_4f8c...d2a1
``
(il prefisso whsec_ più 64 caratteri esadecimali). Ti servirà per verificare le consegne — conservalo insieme alla destinazione, idealmente come variabile d'ambiente sul servizio ricevente.
Tipi di evento
Ci sono sei tipi di evento sottoscrivibili, e tutti e sei vengono consegnati oggi:
- •`ev_signal` — lo scanner ha trovato una nuova opportunità di EV sopra la soglia.
- •`arb_opportunity` — è comparso uno scarto di arbitraggio tra Polymarket e Kalshi su mercati equivalenti.
- •`stop_loss_triggered` — una delle tue protezioni ha superato la soglia ed è scattata.
- •`bot_trade_executed` — uno dei tuoi bot ha aperto o chiuso un'operazione.
- •`whale_move` — un wallet tracciato ha preso una posizione da 25.000 $ in su. Evento globale: lo riceve ogni sottoscrittore, e ogni movimento è deduplicato per non arrivare mai due volte.
- •`resolution_imminent` — un mercato *in cui hai posizione* è entrato nelle ultime 24 ore prima della risoluzione. Per utente, e inviato una volta per mercato.
Gli ultimi due erano sottoscrivibili ma muti: le loro fonti sono query pure, quindi senza memoria di ciò che è già stato inviato un cron riemetterebbe gli stessi eventi a ogni giro. Quella memoria ora esiste: è ciò che li ha resi consegnabili.
Iscrivi ogni destinazione solo agli eventi che le interessano. Il tuo pager di reperibilità probabilmente vuole solo stop_loss_triggered; un canale di ricerca potrebbe volere whale_move ed ev_signal; un flusso di automazione che effettua il ribilanciamento potrebbe volere bot_trade_executed.
Payload e header di consegna
Ogni consegna è un POST HTTP con un corpo JSON strutturato così:
``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
}
}
``
Il campo event ti indica il tipo, timestamp è il momento in cui Predite lo ha inviato e data è il payload specifico dell'evento. Insieme al corpo, Predite invia questi header:
- •
X-Predite-Event— il tipo di evento, così puoi instradare senza analizzare il corpo. - •
X-Predite-Timestamp— l'orario di invio (corrisponde altimestampdel corpo). - •
X-Predite-Signature— la firma HMAC-SHA256, formattata comesha256=<hex>. - •
User-Agent: Predite-Webhooks/1.0.
Il tuo endpoint dovrebbe rispondere rapidamente con un qualsiasi stato 2xx. La consegna va in timeout dopo 5 secondi, quindi conferma in fretta ed esegui il lavoro pesante in modo asincrono — restituisci 200, poi elabora.
Comportamento di affidabilità
La consegna non è del tipo fire-and-forget dal lato di Predite. Le consegne fallite (una risposta non-2xx, un timeout o un errore di rete) vengono registrate, e i fallimenti transitori 5xx/di rete vengono ritentati con backoff esponenziale. Se un'iscrizione accumula 10 fallimenti consecutivi, Predite la disabilita automaticamente (stato disabled_by_failures) per smettere di martellare un endpoint inattivo. Vedrai il motivo del fallimento in Impostazioni → Webhook, e riabilitare l'iscrizione azzera il contatore dei fallimenti. Ogni iscrizione tiene inoltre traccia dell'ultimo successo, dell'ultimo fallimento e del totale delle consegne, così puoi controllarne lo stato a colpo d'occhio.
Verificare le firme dei webhook
Poiché l'URL del tuo webhook è solo un endpoint HTTP, chiunque ne venga a conoscenza potrebbe inviare in POST eventi falsi. L'header della firma è il modo in cui dimostri che una consegna proviene davvero da Predite e non è stata manomessa durante il transito.
Lo schema è lineare: Predite calcola HMAC-SHA256(raw_request_body, your_secret) e invia il digest esadecimale in X-Predite-Signature come sha256=<digest>. Dal tuo lato ricalcoli lo stesso HMAC sul corpo grezzo, non analizzato e lo confronti. La parte del "corpo grezzo" è importante — se il tuo framework analizza il JSON e tu lo riserializzi, gli spazi e l'ordine delle chiavi possono differire e la firma non corrisponderà. Cattura i byte prima dell'analisi.
Un confronto a tempo costante 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) ```
Un ricevitore Flask minimale che rifiuta qualsiasi cosa non firmata:
```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 ```
Usa sempre un confronto a tempo costante (hmac.compare_digest, non ==), così da non far trapelare i byte della firma attraverso il timing. E rifiuta la richiesta prima di svolgere qualsiasi lavoro reale se la verifica fallisce — quell'unico controllo è la differenza tra una pipeline affidabile e una porta aperta.
Costruire una pipeline di alerting personalizzata
Mettiamo tutto insieme con un esempio concreto: vuoi che ogni segnale EV ad alta convinzione e ogni trigger di stop-loss arrivino su Slack, e vuoi che gli stop-loss avviino in aggiunta un workflow n8n che registra l'evento e (facoltativamente) chiude la posizione tramite la tua integrazione di broker.
- Crea due iscrizioni webhook in Impostazioni → Webhook. Una punta all'URL del tuo webhook in entrata di Slack e si iscrive a
ev_signal. L'altra punta al tuo nodo webhook di n8n e si iscrive astop_loss_triggered. Ciascuna ottiene il proprio segreto. - Per Slack, l'approccio più pulito è un piccolo relay (una funzione serverless funziona bene): verifica la firma di Predite, formatta i
datain un messaggio Slack e li pubblica sull'URL di Slack. Il relay ti permette di filtrare — per esempio, inoltrare solo i segnali in cuidata.edge_pp >= 10— così non sei rumoroso nel canale. I mercati con un edge piccolo vengono scartati; solo quelli forti avvisano il team. - Per n8n, metti per primo un nodo trigger Webhook, poi un nodo Function che esegue lo stesso controllo HMAC sul corpo grezzo prima che qualsiasi cosa a valle venga eseguita. Dopo la verifica, ramifica su
dataper registrare l'evento e chiamare il tuo broker. Zapier segue lo stesso schema con uno step Code che esegue la verifica. - Aggiungi l'API REST per il contesto. Quando arriva un evento
stop_loss_triggered, il tuo flusso n8n può chiamare immediatamenteGET /api/v1/portfoliocon la tua chiave API per estrarre l'intero set di posizioni attuali, così l'alert è arricchito con il P&L live anziché con i soli dati del trigger. È il pattern read-on-demand che lavora insieme al pattern push. - Rendilo idempotente. Le reti ritentano; la stessa consegna può arrivare due volte. Aggancia le tue azioni a valle a qualcosa di stabile (l'evento più il suo timestamp, oppure un ID all'interno di
data), così una consegna duplicata non agisce due volte. - Monitora il monitor. Controlla periodicamente Impostazioni → Webhook per
disabled_by_failures, e fai sì che il tuo relay registri ogni firma rifiutata — un picco improvviso significa o che il tuo segreto è stato ruotato oppure che qualcuno sta sondando il tuo endpoint.
Con questo in posizione hai un ciclo chiuso: lo scanner e il motore di rischio di Predite inviano gli eventi nell'istante in cui scattano, i tuoi relay li verificano e li filtrano, e le tue chiamate REST completano lo stato live su richiesta — il tutto senza un singolo loop di polling che brucia quota.
Se sei sul piano Bot, il modo più rapido per far scattare il meccanismo è andare su Impostazioni → Webhook, puntare un'iscrizione di test a un canale Discord usa e getta, iscriverla a ev_signal e osservare il primo segnale reale arrivare come POST JSON — poi collega lo snippet di verifica qui sopra e sarai a un solo relay dalla produzione.