Watch-IP

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

email
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.csv

Antwoord

{
  "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.csv

Registratiecontrole

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