Dashboard โ†’

API Reference

MailVerify REST API

A simple JSON API for email verification. All requests hit https://api.mailverify.co.uk and return JSON. Every response includes server_region: "GB" โ€” your data stays in the UK. SMTP checks to recipient mail servers are made from UK IP addresses.

GB ServersREST / JSONGDPR NativeISO 8601 timestamps
Also onMailVerify on RapidAPI โ€” open listing

Quickstart

Get a verified result in one request. Grab your API key from the dashboard and run:

curl -s "https://api.mailverify.co.uk/v1/verify" \
  -H "Authorization: Bearer mv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'

Same request with JavaScript (Node 18+ / modern runtimes), PHP, or Python:

const res = await fetch("https://api.mailverify.co.uk/v1/verify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MAILVERIFY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "[email protected]" }),
});
const data = await res.json();
console.log(data);

Response:

{
  "job_id": "3f2a1b4c-...",
  "email": "[email protected]",
  "result": "valid",
  "is_catch_all": false,
  "is_disposable": false,
  "is_role": false,
  "is_free_email": false,
  "did_you_mean": null,
  "mx_host": "aspmx.l.google.com",
  "smtp_code": 250,
  "checked_at": "2026-04-05T14:22:00.000Z",
  "server_region": "GB"
}

Authentication

All endpoints (except GET /health) require an API key passed as a Bearer token.

Authorization: Bearer mv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys take the form mv_live_<random>. Generate them from the dashboard. Keys are hashed with SHA-256 before storage โ€” MailVerify never holds your plain key.
PlanBilling modelLimit
free25 free verifications / month25 / month
paygPer-verification creditsUntil credits run out
subscriptionMonthly allowanceResets on the 1st of each month
enterpriseCustomNo hard cap

Rate limits

Throughput limits apply per API key. Free, Starter, Pay-as-you-go, and Subscription use a Redis sliding window; X-RateLimit-* headers are returned when a limit is in effect. Enterprise (including RapidAPI with your own MailVerify account and API key) bypasses these throughput limits entirely โ€” no cap on POST /v1/verify or other standard endpoints.

PlanLimitWindow
free / starter1 request1 second
payg / subscription60 requests60 seconds
enterpriseNo capโ€”
POST /v1/verify/bulk (all plans)10 requests60 seconds
Bulk submission rate limits still apply to Enterprise. Fair-use and abuse protections may apply to any plan if traffic degrades the service for others.

When a limit is exceeded you receive a 429 with a Retry-After header indicating seconds to wait.

HeaderDescription
X-RateLimit-LimitMax requests in the window
X-RateLimit-RemainingRequests left before throttle
X-RateLimit-ResetUnix timestamp when window resets
Retry-AfterSeconds to wait (only on 429)

Enterprise keys may omit X-RateLimit-* headers on endpoints where no throughput limit applies.

POST /v1/verify

Verify a single email address. Results for known addresses are returned from cache immediately.

POST/v1/verify
{
  "email": "[email protected]"
}
FieldTypeRequiredDescription
emailstringrequiredThe email address to verify

200 โ€” Result ready

FieldTypeDescription
job_iduuidUnique ID for this verification job
emailstringEcho of the verified address
resultstringvalidMailbox exists and accepted the SMTP probeinvalidMailbox does not exist or was permanently rejectedcatch_allDomain accepts all addresses โ€” individual existence unverifiabledisposableKnown temporary / throwaway email domainroleRole-based address (info@, support@, admin@โ€ฆ)unknownMail server gave a temporary or ambiguous response โ€” will be re-checked
is_catch_allbooleanDomain accepts any local-part
is_disposablebooleanKnown disposable / temporary domain
is_rolebooleanRole-based address (info@, support@โ€ฆ)
is_free_emailbooleanFree / consumer email provider (Gmail, Yahoo, etc.)
did_you_meanstring | nullSuggested correction for common domain typos, e.g. gmal.com โ†’ gmail.com
mx_hoststring | nullHighest-priority MX record for the domain
smtp_codeinteger | nullSMTP RCPT TO response code
checked_atISO 8601When the check completed
server_regionstringAlways GB
Verification is synchronous โ€” you always receive a 200 with the result immediately. Use GET /v1/results/:jobId to look up historical results by job ID.

POST /v1/verify/bulk

Submit up to 5,000 email addresses in a single request. All addresses are syntax-validated and deduplicated before enqueueing. Results are delivered via webhook or polled using the batch results endpoint.

POST/v1/verify/bulk
{
  "emails": [
    "[email protected]",
    "[email protected]",
    "[email protected]"
  ],
  "webhook_url": "https://yourapp.co.uk/webhooks/mailverify"
}
FieldTypeRequiredDescription
emailsstring[]required1 โ€“ 5,000 email addresses
webhook_urlstringoptionalHTTPS URL to POST results to when the batch completes

202 โ€” Accepted

{
  "batch_id": "9a1c2d3e-...",
  "total": 3,
  "queued": 3,
  "estimated_completion_seconds": 9
}
Credits are charged upfront for the full batch. Same-domain addresses are staggered (3 s gap) to avoid hammering a single MX server โ€” this is reflected in estimated_completion_seconds.

GET /v1/results/:jobId

Retrieve the current status and result of a single verification job. Returns 404 if the job ID does not belong to your API key.

GET/v1/results/:jobId

Response

{
  "job_id": "3f2a1b4c-...",
  "email": "[email protected]",
  "status": "complete",
  "result": "valid",
  "is_catch_all": false,
  "is_disposable": false,
  "is_role": false,
  "is_free_email": false,
  "did_you_mean": null,
  "mx_host": "aspmx.l.google.com",
  "smtp_code": 250,
  "smtp_message": "250 OK",
  "server_region": "GB",
  "created_at": "2026-04-05T14:22:00.000Z",
  "completed_at": "2026-04-05T14:22:03.411Z"
}
statusMeaning
pendingJob is queued or actively being checked
completeVerification finished โ€” result field is populated
failedAll retry attempts exhausted without a definitive answer

GET /v1/results/batch/:batchId

Retrieve progress and paginated results for a bulk batch. Supports ?page and ?limit (default 100, max 1000).

GET/v1/results/batch/:batchId
Query paramDefaultDescription
page1Page number (1-indexed)
limit100Results per page (max 1000)

Response

{
  "batch_id": "9a1c2d3e-...",
  "status": "complete",
  "total": 3,
  "completed": 3,
  "failed": 0,
  "summary": {
    "total": 3,
    "completed": 3,
    "failed": 0,
    "valid": 1,
    "invalid": 0,
    "catch_all": 1,
    "disposable": 1,
    "role": 0,
    "unknown": 0
  },
  "pagination": { "page": 1, "limit": 100, "total": 3 },
  "results": [
    {
      "job_id": "...",
      "email": "[email protected]",
      "status": "complete",
      "result": "valid",
      "is_catch_all": false,
      "is_disposable": false,
      "is_role": false,
      "mx_host": "aspmx.l.google.com",
      "smtp_code": 250,
      "server_region": "GB",
      "completed_at": "2026-04-05T14:22:03.411Z"
    }
  ]
}

GET /v1/usage

Returns billing period consumption for the authenticated API key.

GET/v1/usage
{
  "plan": "subscription",
  "credits_used": 1420,
  "credits_remaining": 48580,
  "monthly_limit": 50000,
  "period_resets_at": "2026-05-01T00:00:00.000Z",
  "server_region": "GB"
}

Webhooks

Register HTTPS endpoints to receive push notifications when batches complete. MailVerify retries failed deliveries up to 3 times with exponential backoff (1 s โ†’ 2 s โ†’ 4 s).

POST/v1/webhooks
{
  "url": "https://yourapp.co.uk/webhooks/mailverify",
  "events": ["batch.complete"]
}
FieldTypeRequiredDescription
urlstringrequiredHTTPS URL to receive events
eventsstring[]optionalDefaults to ["batch.complete"]. Also accepts "job.complete", "job.failed"
DELETE/v1/webhooks/:id

Remove a registered webhook. Returns 204 on success.

Webhook payload

{
  "event": "batch.complete",
  "batch_id": "9a1c2d3e-...",
  "total": 500,
  "server_region": "GB",
  "delivered_at": "2026-04-05T14:40:00.000Z",
  "results": [ ... ]
}

Signature verification

Every delivery includes an X-MailVerify-Signature header. Verify it with your webhook secret from the dashboard before trusting the payload.

const crypto = require("node:crypto");

const secret = process.env.MAILVERIFY_WEBHOOK_SECRET;
const signature = req.headers["x-mailverify-signature"];
const rawBody = req.rawBody; // use middleware to capture raw bytes

const expected = crypto
  .createHmac("sha256", secret)
  .update(rawBody)
  .digest("hex");

if (signature !== `sha256=${expected}` && signature !== expected) {
  throw new Error("Invalid webhook signature");
}

Error codes

All error responses share a consistent shape:

{
  "error": "Insufficient credits",
  "code": "INSUFFICIENT_CREDITS"
}
HTTPcodeMeaning
401INVALID_API_KEYMissing, malformed, or revoked API key
402INSUFFICIENT_CREDITSNo credits remaining or monthly limit reached
422INVALID_EMAILEmail address fails syntax validation
422BATCH_TOO_LARGEBatch exceeds 5,000 email limit
429RATE_LIMITEDToo many requests โ€” check Retry-After
404JOB_NOT_FOUNDJob or batch ID not found (or belongs to a different key)
500INTERNAL_ERRORUnexpected server error โ€” our team is automatically alerted
Result vs error: a result of unknown is not an error. It means the mail server gave a temporary or ambiguous response (greylisting, rate-limiting). The address will be re-checked automatically on next request.
๐Ÿ‡ฌ๐Ÿ‡ง All data processed in the UK