API-referentie e-mailcontrole
POST/v1/email/validate
Controleert één e-mailadres: syntaxis, bekende wegwerp-/tijdelijke domeinen, de mailserver (MX) van het domein, waarschijnlijke typefouten en functionele adressen, en geeft een oordeel accept/review/reject met de redenen erachter. Stuur een JSON-body { "email": "user@example.com" }. Bedoeld om aan te roepen vanuit het eigen JavaScript van een registratieformulier, met hetzelfde model van een publiceerbare, origin-gebonden sleutel als GET /v1/geo. Gebruik voor veel adressen of een registratiebeslissing aan de serverkant de endpoints voor batch, lijstjobs en registratiecontrole verderop op deze pagina. Alle controles gebeuren op domein- en adresniveau: er wordt geen mailbox benaderd, dus geen enkel resultaat stelt vast dat een mailbox bestaat, van wie die is of dat e-mail er aankomt.
Authenticatie
POST /v1/email/validate accepteert een publiceerbare, origin-gebonden sleutel via de header X-Api-Key — hetzelfde sleuteltype en hetzelfde model van toegestane origins als GET /v1/geo. Alle andere endpoints op deze pagina (batch, lijstjobs, registratiecontrole) accepteren veel adressen of een willekeurig IP, vereisen daarom je geheime sleutel via Authorization: Bearer en mogen alleen vanuit je backend worden aangeroepen; een publiceerbare sleutel krijgt daar een 403.
Antwoordvelden
- Het adres dat je stuurde, precies zo teruggegeven als het is ingediend.
- domain
- Het domein in kleine letters (geïnternationaliseerde domeinen in punycode), of null als syntaxValid false is.
- syntaxValid
- Of het adres syntactisch geldig is. Geen controle op het bestaan van de mailbox of op bezorgbaarheid.
- isDisposable
- Of het domein overeenkomt met een bekende aanbieder van wegwerp-/tijdelijke e-mail — rechtstreeks, via een bovenliggend domein of via een mailserver waarvan bekend is dat die wegwerpdomeinen bedient. Altijd false als syntaxValid false is.
- normalizedEmail
- Opgeschoond adres in kleine letters met het domein in punycode, of null als syntaxValid false is.
- canonicalEmail
- normalizedEmail zonder eventuele +tag (en voor Gmail zonder punten en met googlemail.com omgezet naar gmail.com) — sla dit op om dezelfde persoon te herkennen die zich twee keer registreert. Null als syntaxValid false is.
- disposableSource
- Waarom isDisposable true is: list (het domein zelf staat op de lijst), parent_domain (een bovenliggend domein staat op de lijst, bijv. a.b.mailinator.com) of mx_host (de mailserver van het domein bedient bekende wegwerpdomeinen). Null als isDisposable false is.
- isFreeProvider
- Of het domein een mailboxaanbieder voor consumenten is, zoals gmail.com of outlook.com. Alleen ter informatie — verandert nooit het oordeel.
- isRoleAccount
- Of het lokale deel een functioneel adres is, zoals admin@, info@ of noreply@.
- didYouMean
- Een waarschijnlijke correctie van een typefout in het hele adres (john@gmial.com → john@gmail.com), of null. Een domein met een eigen werkende mailserver wordt niet „gecorrigeerd”, tenzij het één bewerking verwijderd is van een veelgebruikte aanbieder.
- mx.status
- found (er bestaan MX-records), implicit (geen MX, maar wel een A/AAAA-record), none (geen van beide), null_mx (het domein verklaart geen e-mail te accepteren), no_domain (het domein bestaat niet) of unknown (de opzoeking is mislukt of verlopen). Het hele mx-object is null als syntaxValid false is.
- mx.host
- De MX-host met de laagste voorkeurswaarde als status found is, anders null. Zegt niets over het bestaan van de afzonderlijke mailbox.
- verdict
- accept, review of reject — de zwaarste van de voorwaarden in reasons. Een aanbeveling voor je eigen beleid, geen definitieve beslissing.
- reasons
- Elke voorwaarde die van toepassing was, de zwaarste eerst; leeg als verdict accept is. Zie „Oordelen en redencodes” hieronder.
Foutmeldingen
- 400
- Ongeldige request-body. Batch: empty_batch of batch_too_large (meer dan 100 adressen). Registratiecontrole: invalid_ip. Lijstjobs: invalid_body, empty_job of job_too_large (meer dan 100.000 adressen). Een ongeldig e-mailadres levert nooit een 400 op — het is een resultaat met oordeel reject en reden invalid_syntax.
- 401
- Ontbrekende of ongeldige API-sleutel.
- 403
- POST /v1/email/validate: de Origin/Referer van het verzoek staat niet op de lijst met toegestane origins van de sleutel (er wordt geen CORS-header gezet, dus de browser blokkeert het antwoord ongeacht de JSON-body). Endpoints met geheime sleutel: er is een publiceerbare sleutel gebruikt.
- 404
- job_not_found — geen lijstjob met dit id voor je account.
- 409
- job_not_completed — de job staat nog in de wachtrij of wordt verwerkt, of is mislukt; resultaten zijn pas te downloaden als de job voltooid is.
- 410
- job_expired — de geüploade adressen en resultaten van de job zijn verwijderd (7 dagen na aanmaken).
- 413
- body_too_large — een CSV-upload voor een job groter dan 10 MB.
- 415
- Een lijstjob-body die geen JSON en geen text/csv is.
- 429
- De rate limit per minuut, of monthly_limit_exceeded als de maandelijkse verzoeklimiet van het abonnement zou worden overschreden — zie fouten en rate limits om ze te onderscheiden. Een batch of lijstjob die de limiet zou overschrijden, wordt in zijn geheel afgewezen voordat er iets wordt verwerkt.
- 503
- job_storage_unavailable — een lijstjob kon niet worden opgeslagen. Er is niets in rekening gebracht; probeer het opnieuw.
Voorbeeld
Verzoek
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"}'Antwoord
{
"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"
]
}Oordelen en redencodes
Elke voorwaarde legt een minimaal oordeel op; de zwaarste wint, en reasons somt ze allemaal op, de zwaarste eerst. Of een adres bij een gratis aanbieder acceptabel is, bepaal je zelf — het verandert nooit het oordeel.
- invalid_syntax
- Reject. Het adres is syntactisch ongeldig (lengtelimieten, toegestane tekens, plaatsing van punten, een echt domein in plaats van een letterlijk IP). Lokale delen tussen aanhalingstekens worden ook afgewezen — toegestaan, maar bij registratie vrijwel nooit gebruikt.
- domain_not_found
- Reject. Het domein bestaat niet in DNS.
- no_mail_server
- Reject. Het domein verklaart geen e-mail te accepteren (een „null MX”), of heeft geen MX- en geen A/AAAA-records.
- disposable_domain
- Reject. Het domein, een bovenliggend domein of de mailserver van het domein komt overeen met bekende aanbieders van wegwerp-/tijdelijke e-mail — zie disposableSource.
- typo_suspected
- Review. didYouMean bevat een waarschijnlijke correctie, zoals gmial.com → gmail.com.
- role_account
- Review. Het lokale deel is een functioneel adres zoals admin@, info@, support@ of noreply@, meestal gedeeld in plaats van persoonlijk.
- implicit_mx
- Review. Het domein heeft geen MX-record; e-mail zou terugvallen op het A/AAAA-adres, wat zelden voorkomt bij echte maildomeinen.
- dns_unavailable
- Review. De DNS-opzoeking is mislukt of verlopen, dus de mailconfiguratie van het domein is onbekend (mx.status: unknown). Geen bewijs tegen het adres.
Batchcontrole
POST/v1/email/batch
Controleert 1–100 adressen in één verzoek met dezelfde controles en dezelfde resultaatvorm als POST /v1/email/validate, één resultaat per adres in de invoervolgorde. Stuur { "emails": [...] }. Adressen op hetzelfde domein delen één DNS-opzoeking. Telt één verzoek per adres mee voor de maandlimiet van je abonnement; een batch die de limiet zou overschrijden, wordt in zijn geheel afgewezen met 429 monthly_limit_exceeded. Voor de rate limit per minuut telt de hele aanroep als één verzoek. Alleen met geheime sleutel.
Verzoek
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"]}'Antwoord
{
"results": [
{ "email": "user@gmail.com", "verdict": "accept", "reasons": [], ... },
{ "email": "someone@mailinator.com", "isDisposable": true, "disposableSource": "list", "verdict": "reject", "reasons": ["disposable_domain"], ... }
]
}E-maillijstjobs
POST/v1/email/jobs
Controleert tot 100.000 adressen op de achtergrond. Stuur JSON { "emails": [...], "filename": "..." } of een text/csv-body van maximaal 10 MB: de kolom met de kop email (ook e-mail, email address, email_address of mail, niet hoofdlettergevoelig) wordt gebruikt, of de eerste kolom als geen kop er een benoemt; komma, puntkomma en tab als scheidingsteken worden herkend. De job wordt bij het aanmaken in rekening gebracht — één verzoek per uniek adres (exacte overeenkomst), dus dubbele adressen worden gecontroleerd maar één keer gerekend — en een lijst die je maandlimiet zou overschrijden, wordt in zijn geheel afgewezen. Als een deel van een job na herhaalde pogingen niet verwerkt kan worden, wordt de job als failed gemarkeerd en worden de credits voor het niet-verwerkte deel teruggegeven. Antwoordt met 202 en het job-object. Alleen met geheime sleutel; dezelfde jobs kun je ook uploaden en downloaden via de dashboardpagina Email lists.
Verzoek
curl https://api.watch-ip.com/v1/email/jobs \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: text/csv" \
--data-binary @newsletter.csvAntwoord
{
"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"
}Jobstatus
GET/v1/email/jobs/{id}
Geeft hetzelfde job-object terug. Vraag het periodiek op tot status completed is; opvragen wordt niet in rekening gebracht.
- status
- queued → processing → completed. failed: een deel van de lijst kon na herhaalde pogingen niet worden verwerkt; er zijn geen resultaten en de credits voor het niet-verwerkte deel zijn teruggegeven. expired: de geüploade adressen en resultaten zijn 7 dagen na aanmaken verwijderd.
- total
- Ingediende rijen, inclusief dubbele.
- credits
- In rekening gebrachte verzoeken: één per uniek adres.
- processed
- Tot nu toe gecontroleerde rijen.
- summary
- Na voltooiing: aantal rijen per oordeel (accept/review/reject) en per opgetreden redencode. Daarvoor null.
- expiresAt
- Wanneer de geüploade adressen en resultaten worden verwijderd.
Jobresultaten
GET/v1/email/jobs/{id}/results
Downloadt een CSV met één rij per ingediende rij, in de volgorde van indienen — inclusief dubbele. Kolommen: 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 is gescheiden door puntkomma's, lege cellen betekenen null en mx_status is leeg voor syntactisch ongeldige adressen (er is niets opgezocht). Een ongeldig adres dat begint met =, +, - of @ krijgt een apostrof ervoor, zodat spreadsheetprogramma's het niet als formule uitvoeren. 409 zolang de job niet voltooid is, 410 zodra hij verlopen is. Downloaden wordt niet in rekening gebracht.
Verzoek
curl https://api.watch-ip.com/v1/email/jobs/5b0d6f5e-8a51-4f7e-9d0e-2c1f3f7f8a11/results \
-H "Authorization: Bearer YOUR_SECRET_KEY" -o results.csvRegistratiecontrole
POST/v1/signup/check
Beoordeelt een registratie op basis van e-mailadres en IP in één aanroep, vanuit je backend wanneer het registratieformulier wordt verzonden (een in de browser berekend oordeel kan worden vervalst). Stuur { "email": "...", "ip": "...", "userAgent": "..." } — userAgent is optioneel. Het antwoord bevat het volledige resultaat van de e-mailcontrole (email), het land en ASN van het IP en — bij abonnementen met beveiligingsgegevens — de VPN-/Tor-/datacenter-/dreigingsindicatoren (ip), de leeftijd van het e-maildomein in dagen volgens openbare registratiegegevens (RDAP) (emailDomainAgeDays, null als onbekend en voor bekende consumentenaanbieders), een score van 0–100 (de som van de gewichten van de redenen hieronder, met een maximum van 100) en een oordeel: reject vanaf 60, review vanaf 25, anders accept. Bij een abonnement zonder beveiligingsgegevens is ip.security null, gebruikt de score alleen de e-mailsignalen en bevat reasons ip_security_unavailable. Telt als één verzoek. Alleen met geheime sleutel.
- invalid_syntax
- 70
- domain_not_found · no_mail_server · disposable_domain
- elk 60
- ip_tor · ip_threat
- elk 40
- ip_vpn
- 25
- new_domain
- 25 — het e-maildomein is minder dan 30 dagen geleden geregistreerd
- ip_datacenter
- 20 — alleen als het IP niet al een VPN- of Tor-exit is
- automated_user_agent
- 20 — de opgegeven userAgent lijkt op een bot of script
- typo_suspected
- 15
- role_account · implicit_mx
- elk 10
- dns_unavailable
- 5
- ip_security_unavailable
- 0 — ter informatie: dit abonnement bevat geen beveiligingsgegevens
Verzoek
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"}'Antwoord
{
"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"]
}Limieten
Elk gecontroleerd adres telt als één verzoek voor de gedeelde maandlimiet van je abonnement — een enkele controle, elk adres in een batch, elk uniek adres in een lijstjob — en een registratiecontrole is één verzoek. Batches en lijstjobs worden in hun geheel geaccepteerd of afgewezen, nooit gedeeltelijk uitgevoerd. De rate limit per minuut telt HTTP-aanroepen, geen items: een batch van maximaal 100 IP's of e-mailadressen telt als één verzoek, terwijl elk item nog steeds apart meetelt voor de maandelijkse verzoeklimiet. Uploads en resultaten van lijstjobs worden 7 dagen na het aanmaken van de job verwijderd. DNS-opzoekingen hebben een kort tijdsbudget: als er een mislukt of verloopt, valt het resultaat terug op mx.status unknown en reden dns_unavailable in plaats van een fout. Gegevens over wegwerpdomeinen worden samengevoegd uit drie door de community onderhouden open-sourcelijsten, om de paar uur ververst en aangevuld met mailservers waarvan is waargenomen dat ze vermelde domeinen bedienen — zie gegevensbronnen. Een heel nieuwe wegwerpaanbieder wordt mogelijk nog niet gemarkeerd. Alle controles gebeuren op domein- en adresniveau: er wordt geen mailbox benaderd, dus geen enkel resultaat stelt vast dat een mailbox bestaat, van wie die is of dat e-mail er aankomt.
Gerelateerde pagina's
- AuthenticatiePubliceerbare versus geheime sleutels, origin-binding en rate limits.
- GET /v1/geo-referentieVolledige veldreferentie voor het bezoekersgeolocatie-endpoint.
- Detectie van wegwerp-e-mailMarkeer wegwerp-, verkeerd getypte en e-mailadressen zonder mailserver bij registratie, één voor één of in bulk.
- Preventie van aanmeldmisbruikCombineer e-mail- en IP-signalen tot een registratiebeslissing: accepteren, beoordelen of afwijzen.
- DatabronnenWelke bron elk veld in het antwoord ondersteunt, en de bekende gaten.