Riferimento dell'API di verifica e-mail
POST/v1/email/validate
Verifica un indirizzo e-mail: sintassi, domini usa e getta/temporanei noti, il server di posta (MX) del dominio, probabili refusi e indirizzi di ruolo, e restituisce un verdetto accept/review/reject con i relativi motivi. Invia un body JSON { "email": "user@example.com" }. Pensato per essere chiamato dal JavaScript del modulo di registrazione, con lo stesso modello di chiave pubblicabile limitata per origine di GET /v1/geo. Per molti indirizzi o per una decisione di registrazione lato server, usa gli endpoint batch, dei job di lista e di verifica della registrazione descritti più in basso. Tutti i controlli sono a livello di dominio e di indirizzo: nessuna casella di posta viene contattata, quindi nessun risultato stabilisce che una casella esista, chi ne sia il titolare o che la posta vi arriverà.
Autenticazione
POST /v1/email/validate accetta una chiave pubblicabile limitata per origine tramite l'header X-Api-Key — lo stesso tipo di chiave e lo stesso modello di origini consentite di GET /v1/geo. Tutti gli altri endpoint di questa pagina (batch, job di lista, verifica della registrazione) accettano molti indirizzi o un IP arbitrario, quindi richiedono la tua chiave segreta tramite Authorization: Bearer e vanno chiamati solo dal tuo backend; una chiave pubblicabile riceve un 403.
Campi della risposta
- L'indirizzo inviato, restituito esattamente come è stato inviato.
- domain
- Il dominio in minuscolo (domini internazionalizzati in punycode), oppure null quando syntaxValid è false.
- syntaxValid
- Se l'indirizzo è sintatticamente valido. Non è una verifica dell'esistenza della casella né della recapitabilità.
- isDisposable
- Se il dominio corrisponde a un provider di e-mail usa e getta/temporanee noto — direttamente, tramite un dominio padre o tramite un server di posta noto per servire domini usa e getta. Sempre false quando syntaxValid è false.
- normalizedEmail
- Indirizzo ripulito e in minuscolo con il dominio in punycode, oppure null quando syntaxValid è false.
- canonicalEmail
- normalizedEmail senza eventuale +tag (e, per Gmail, senza punti e con googlemail.com ricondotto a gmail.com) — conservalo per individuare la stessa persona che si registra due volte. Null quando syntaxValid è false.
- disposableSource
- Perché isDisposable è true: list (il dominio stesso è in lista), parent_domain (un dominio padre è in lista, ad es. a.b.mailinator.com) o mx_host (il server di posta del dominio serve domini usa e getta noti). Null quando isDisposable è false.
- isFreeProvider
- Se il dominio è un provider di posta per consumatori come gmail.com o outlook.com. Solo informativo — non modifica mai il verdetto.
- isRoleAccount
- Se la parte locale è un indirizzo di ruolo come admin@, info@ o noreply@.
- didYouMean
- Una probabile correzione del refuso sull'intero indirizzo (john@gmial.com → john@gmail.com), oppure null. Un dominio con un proprio server di posta funzionante non viene «corretto», a meno che non sia a una modifica di distanza da un provider comune.
- mx.status
- found (esistono record MX), implicit (nessun MX, ma un record A/AAAA), none (né l'uno né l'altro), null_mx (il dominio dichiara di non accettare posta), no_domain (il dominio non esiste) o unknown (la ricerca è fallita o è andata in timeout). L'intero oggetto mx è null quando syntaxValid è false.
- mx.host
- L'host MX con la preferenza più bassa quando status è found, altrimenti null. Non dice nulla sull'esistenza della singola casella di posta.
- verdict
- accept, review o reject — la più grave delle condizioni in reasons. Una raccomandazione per la tua policy, non una decisione definitiva.
- reasons
- Ogni condizione riscontrata, dalla più grave; vuoto quando verdict è accept. Vedi «Verdetti e codici motivo» più sotto.
Errori
- 400
- Body della richiesta malformato. Batch: empty_batch o batch_too_large (più di 100 indirizzi). Verifica della registrazione: invalid_ip. Job di lista: invalid_body, empty_job o job_too_large (più di 100.000 indirizzi). Un indirizzo e-mail non valido non produce mai un 400 — è un risultato con verdetto reject e motivo invalid_syntax.
- 401
- Chiave API mancante o non valida.
- 403
- POST /v1/email/validate: l'Origin/Referer della richiesta non è nell'elenco delle origini consentite della chiave (non viene impostato alcun header CORS, quindi il browser blocca la risposta indipendentemente dal body JSON). Endpoint con chiave segreta: è stata usata una chiave pubblicabile.
- 404
- job_not_found — nessun job di lista con questo id per il tuo account.
- 409
- job_not_completed — il job è ancora in coda o in elaborazione, oppure è fallito; i risultati sono scaricabili solo a job completato.
- 410
- job_expired — gli indirizzi caricati e i risultati del job sono stati eliminati (7 giorni dopo la creazione).
- 413
- body_too_large — un caricamento CSV di un job superiore a 10 MB.
- 415
- Un body di job di lista che non è né JSON né text/csv.
- 429
- Il limite di frequenza al minuto, oppure monthly_limit_exceeded quando verrebbe superato il limite mensile di richieste del piano — vedi errori e limiti di frequenza per distinguerli. Un batch o un job di lista che supererebbe il limite viene rifiutato per intero, prima di qualsiasi elaborazione.
- 503
- job_storage_unavailable — non è stato possibile memorizzare un job di lista. Non è stato addebitato nulla; riprova.
Esempio
Richiesta
curl https://api.watch-ip.com/v1/email/validate \
-H "X-Api-Key: YOUR_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "John.Doe+news@Gmial.com"}'Risposta
{
"email": "John.Doe+news@Gmial.com",
"domain": "gmial.com",
"syntaxValid": true,
"isDisposable": false,
"normalizedEmail": "john.doe+news@gmial.com",
"canonicalEmail": "john.doe@gmial.com",
"disposableSource": null,
"isFreeProvider": false,
"isRoleAccount": false,
"didYouMean": "john.doe+news@gmail.com",
"mx": {
"status": "none",
"host": null
},
"verdict": "reject",
"reasons": [
"no_mail_server",
"typo_suspected"
]
}Verdetti e codici motivo
Ogni condizione imposta un verdetto minimo; prevale la più grave, e reasons le elenca tutte, dalla più grave. Se un indirizzo di un provider gratuito sia accettabile lo decidi tu — non modifica mai il verdetto.
- invalid_syntax
- Reject. L'indirizzo non è sintatticamente valido (limiti di lunghezza, caratteri consentiti, posizione dei punti, un dominio reale anziché un IP letterale). Anche le parti locali tra virgolette vengono rifiutate — lecite ma di fatto mai usate in registrazione.
- domain_not_found
- Reject. Il dominio non esiste nel DNS.
- no_mail_server
- Reject. Il dominio dichiara di non accettare posta (un «null MX»), oppure non ha né record MX né A/AAAA.
- disposable_domain
- Reject. Il dominio, un dominio padre o il server di posta del dominio corrisponde a provider di e-mail usa e getta/temporanee noti — vedi disposableSource.
- typo_suspected
- Review. didYouMean contiene una probabile correzione, ad esempio gmial.com → gmail.com.
- role_account
- Review. La parte locale è un indirizzo di ruolo come admin@, info@, support@ o noreply@, di solito condiviso anziché personale.
- implicit_mx
- Review. Il dominio non ha un record MX; la posta ripiegherebbe sul suo indirizzo A/AAAA, cosa rara per i domini di posta reali.
- dns_unavailable
- Review. La ricerca DNS è fallita o è andata in timeout, quindi la configurazione di posta del dominio è sconosciuta (mx.status: unknown). Non è un indizio contro l'indirizzo.
Verifica in batch
POST/v1/email/batch
Verifica da 1 a 100 indirizzi in un'unica richiesta con gli stessi controlli e la stessa forma di risultato di POST /v1/email/validate, un risultato per indirizzo nell'ordine di input. Invia { "emails": [...] }. Gli indirizzi dello stesso dominio condividono un'unica ricerca DNS. Conta una richiesta per indirizzo nel limite mensile del tuo piano; un batch che supererebbe il limite viene rifiutato per intero con 429 monthly_limit_exceeded. Per il limite di frequenza al minuto, l'intera chiamata conta come una sola richiesta. Solo chiave segreta.
Richiesta
curl https://api.watch-ip.com/v1/email/batch \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["user@gmail.com", "someone@mailinator.com"]}'Risposta
{
"results": [
{ "email": "user@gmail.com", "verdict": "accept", "reasons": [], ... },
{ "email": "someone@mailinator.com", "isDisposable": true, "disposableSource": "list", "verdict": "reject", "reasons": ["disposable_domain"], ... }
]
}Job di lista e-mail
POST/v1/email/jobs
Verifica fino a 100.000 indirizzi in background. Invia JSON { "emails": [...], "filename": "..." } oppure un body text/csv di al massimo 10 MB: viene usata la colonna intitolata email (anche e-mail, email address, email_address o mail, senza distinzione tra maiuscole e minuscole), oppure la prima colonna se nessuna intestazione la indica; i separatori virgola, punto e virgola e tabulazione vengono rilevati. Il job viene addebitato alla creazione — una richiesta per indirizzo distinto (corrispondenza esatta), quindi i duplicati vengono verificati ma addebitati una volta — e una lista che supererebbe il tuo limite mensile viene rifiutata per intero. Se una parte del job non può essere elaborata dopo i tentativi ripetuti, il job viene contrassegnato come failed e i crediti della parte non elaborata vengono restituiti. Risponde 202 con l'oggetto job. Solo chiave segreta; gli stessi job possono essere caricati e scaricati anche dalla pagina Email lists della dashboard.
Richiesta
curl https://api.watch-ip.com/v1/email/jobs \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: text/csv" \
--data-binary @newsletter.csvRisposta
{
"id": "5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11",
"status": "queued",
"source": "api",
"filename": null,
"total": 12840,
"credits": 12512,
"processed": 0,
"summary": null,
"createdAt": "2026-09-28T10:00:00.000Z",
"completedAt": null,
"expiresAt": "2026-10-05T10:00:00.000Z"
}Stato del job
GET/v1/email/jobs/{id}
Restituisce lo stesso oggetto job. Interrogalo finché status non è completed; le interrogazioni non vengono addebitate.
- status
- queued → processing → completed. failed: una parte della lista non è stata elaborata dopo i tentativi ripetuti; nessun risultato disponibile e i crediti della parte non elaborata sono stati restituiti. expired: gli indirizzi caricati e i risultati sono stati eliminati 7 giorni dopo la creazione.
- total
- Righe inviate, duplicati inclusi.
- credits
- Richieste addebitate: una per indirizzo distinto.
- processed
- Righe verificate finora.
- summary
- A completamento: numero di righe per verdetto (accept/review/reject) e per ciascun codice motivo riscontrato. Null fino ad allora.
- expiresAt
- Quando vengono eliminati gli indirizzi caricati e i risultati.
Risultati del job
GET/v1/email/jobs/{id}/results
Scarica un CSV con una riga per ogni riga inviata, nell'ordine di invio — duplicati inclusi. Colonne: email, normalized_email, verdict, reasons, syntax_valid, is_disposable, disposable_source, is_free_provider, is_role_account, did_you_mean, mx_status, mx_host. reasons è separato da punto e virgola, le celle vuote indicano null e mx_status è vuoto per gli indirizzi sintatticamente non validi (non è stata fatta alcuna ricerca). Un indirizzo non valido che inizia con =, +, - o @ viene preceduto da un apostrofo, così i fogli di calcolo non lo eseguono come formula. 409 finché il job non è completato, 410 una volta scaduto. Il download non viene addebitato.
Richiesta
curl https://api.watch-ip.com/v1/email/jobs/5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11/results \
-H "Authorization: Bearer YOUR_SECRET_KEY" -o results.csvVerifica della registrazione
POST/v1/signup/check
Assegna un punteggio a una registrazione a partire da indirizzo e-mail e IP in un'unica chiamata, dal tuo backend all'invio del modulo di registrazione (un verdetto calcolato nel browser può essere falsificato). Invia { "email": "...", "ip": "...", "userAgent": "..." } — userAgent è facoltativo. La risposta contiene il risultato completo della verifica e-mail (email), il paese e l'ASN dell'IP e — nei piani che includono i dati di sicurezza — i suoi indicatori VPN/Tor/data center/minaccia (ip), l'età del dominio e-mail in giorni secondo i dati pubblici di registrazione (RDAP) (emailDomainAgeDays, null se sconosciuta e per i grandi provider consumer), un punteggio da 0 a 100 (la somma dei pesi dei motivi qui sotto, con un massimo di 100) e un verdetto: reject da 60 in su, review da 25 in su, altrimenti accept. In un piano senza dati di sicurezza, ip.security è null, il punteggio usa solo i segnali dell'e-mail e reasons include ip_security_unavailable. Conta come una richiesta. Solo chiave segreta.
- invalid_syntax
- 70
- domain_not_found · no_mail_server · disposable_domain
- 60 ciascuno
- ip_tor · ip_threat
- 40 ciascuno
- ip_vpn
- 25
- new_domain
- 25 — il dominio e-mail è stato registrato meno di 30 giorni fa
- ip_datacenter
- 20 — solo se l'IP non è già un'uscita VPN o Tor
- automated_user_agent
- 20 — lo userAgent fornito sembra un bot o uno script
- typo_suspected
- 15
- role_account · implicit_mx
- 10 ciascuno
- dns_unavailable
- 5
- ip_security_unavailable
- 0 — informativo: questo piano non include dati di sicurezza
Richiesta
curl https://api.watch-ip.com/v1/signup/check \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@new-startup.io", "ip": "203.0.113.1"}'Risposta
{
"email": { "email": "jane@new-startup.io", "verdict": "accept", "reasons": [], ... },
"ip": {
"ip": "203.0.113.1",
"country": "US",
"asn": 64500,
"asOrganization": "Example Hosting",
"security": { "isTor": false, "isVpn": false, "isDatacenter": true, "isThreat": false, "isSanctionedNetwork": false }
},
"emailDomainAgeDays": 12,
"score": 45,
"verdict": "review",
"reasons": ["new_domain", "ip_datacenter"]
}Limiti
Ogni indirizzo verificato conta come una richiesta nel limite mensile condiviso del tuo piano — una verifica singola, ogni indirizzo di un batch, ogni indirizzo distinto di un job di lista — e una verifica della registrazione è una richiesta. Batch e job di lista vengono accettati o rifiutati per intero, mai evasi parzialmente. Il limite di frequenza al minuto conta le chiamate HTTP, non gli elementi: un batch fino a 100 IP o indirizzi email conta come una sola richiesta, mentre ogni elemento continua a contare separatamente nel limite mensile di richieste. I caricamenti e i risultati dei job di lista vengono eliminati 7 giorni dopo la creazione del job. Le ricerche DNS hanno un breve budget di tempo: quando una fallisce o va in timeout, il risultato degrada a mx.status unknown e motivo dns_unavailable anziché a un errore. I dati sui domini usa e getta derivano dall'unione di tre liste open source mantenute dalla community, aggiornate ogni poche ore ed estese con i server di posta osservati servire domini in lista — vedi fonti dei dati. Un provider usa e getta molto recente potrebbe non essere ancora segnalato. Tutti i controlli sono a livello di dominio e di indirizzo: nessuna casella di posta viene contattata, quindi nessun risultato stabilisce che una casella esista, chi ne sia il titolare o che la posta vi arriverà.
Pagine correlate
- AutenticazioneChiavi pubblicabili e chiavi segrete, restrizione di origine e limiti di frequenza.
- Riferimento GET /v1/geoRiferimento completo dei campi per l'endpoint di geolocalizzazione del visitatore.
- Rilevamento email usa e gettaSegnala le e-mail di registrazione usa e getta, digitate male o senza server di posta, una alla volta o in blocco.
- Prevenzione degli abusi in fase di registrazioneCombina i segnali dell'e-mail e dell'IP in una decisione di registrazione: accettare, rivedere o rifiutare.
- Fonti datiQuale fonte è alla base di ciascun campo della risposta e le sue lacune note.