Resend e Python: risolvere il blocco Cloudflare WAF (error 1010)
Come risolvere il blocco Cloudflare error 1010 quando chiami api.resend.com da Python urllib: causa, fix per urllib e requests, e come testarlo.
Il problema
Una chiamata Python a api.resend.com/emails torna con 1010 Access Denied. Cloudflare blocca la richiesta prima che arrivi a Resend. Nessun messaggio utile: solo il codice di errore.
Questo è successo sul mio sistema. Ho un'integrazione Resend in produzione da febbraio 2026, parte delle 35+ automazioni che girano ogni giorno su 5 piattaforme.
La causa
urllib, la libreria HTTP di default in Python, invia questo header:
User-Agent: Python-urllib/3.xCloudflare WAF classifica il client come automatico non identificato e blocca con errore 1010 prima che la richiesta raggiunga l'API. Non è un problema di Resend: è la politica del WAF davanti all'endpoint. Si ripete su qualsiasi API protetta da Cloudflare con regole antibot attive.
Come si manifesta
Il sintomo: la chiamata ritorna 1010 dal livello Cloudflare, non dall'API. Questo significa:
- Resend non vede la richiesta
- I log di Resend sono vuoti per quella chiamata
- Il tuo codice riceve un errore HTTP ma nessuna informazione utile sulla causa
Il guasto è silenzioso: urllib non avvisa sul proprio User-Agent, Cloudflare non spiega perché blocca, e l'API non vede nulla.
Il fix per urllib
Aggiungi un header User-Agent esplicito in ogni richiesta:
import urllib.request
import json
payload = json.dumps({"from": "...", "to": "...", "subject": "..."}).encode()
req = urllib.request.Request(
"https://api.resend.com/emails",
data=payload,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"User-Agent": "resend-python/1.0"
}
)
response = urllib.request.urlopen(req)Il valore dello User-Agent non deve essere specifico: Cloudflare cerca un header presente e plausibile, non un formato certificato. L'importante è che non sia il default di urllib.
Il fix per requests
Se usi requests invece di urllib, il problema è meno comune: requests invia python-requests/2.x.x di default, che Cloudflare di norma tollera. Se riscontri comunque il blocco, aggiungi l'header esplicitamente:
import requests
response = requests.post(
"https://api.resend.com/emails",
json=payload,
headers={
"Authorization": f"Bearer {api_key}",
"User-Agent": "resend-python/1.0"
}
)Come diagnosticare prima di modificare il codice
Prima di toccare il codice, verifica la causa con curl:
curl -A "test-client/1.0" \
-H "Authorization: Bearer $RESEND_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from":"...","to":"...","subject":"test"}' \
https://api.resend.com/emailsSe con curl passa e con Python no, il problema è l'User-Agent. Confermato.
Perché succede in produzione e non nel test
Nel test spesso si usa curl o requests con il suo UA di default tollerato. In produzione si finisce per usare urllib perché è nella libreria standard, non richiede dipendenze extra e funziona in Cloud Run o Lambda senza install aggiuntivi.
La divergenza User-Agent tra ambiente di test e produzione è sufficiente a far passare il test e bloccare la produzione. Basta un header.
La regola generale
Ogni chiamata a un'API con WAF attivo: controlla cosa manda il client, non solo cosa risponde l'API. Il WAF intercetta prima che la logica applicativa veda la richiesta. Gli errori sembrano venire dall'API ma la causa è a monte.
Sul mio sistema ho aggiunto questi controlli alla checklist di ogni nuova integrazione:
- User-Agent esplicito su ogni client HTTP che chiama API esterne
- test in isolamento prima dell'integrazione nel sistema principale
- log del corpo completo della risposta in caso di 4xx, non solo lo status code
Il controllo è costato meno di 5 minuti. L'assenza del controllo mi aveva già bloccato una notifica importante.