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.
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
mv_live_<random>. Generate them from the dashboard. Keys are hashed with SHA-256 before storage โ MailVerify never holds your plain key.| Plan | Billing model | Limit |
|---|---|---|
free | 25 free verifications / month | 25 / month |
payg | Per-verification credits | Until credits run out |
subscription | Monthly allowance | Resets on the 1st of each month |
enterprise | Custom | No 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.
| Plan | Limit | Window |
|---|---|---|
free / starter | 1 request | 1 second |
payg / subscription | 60 requests | 60 seconds |
enterprise | No cap | โ |
| POST /v1/verify/bulk (all plans) | 10 requests | 60 seconds |
When a limit is exceeded you receive a 429 with a Retry-After header indicating seconds to wait.
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests in the window |
X-RateLimit-Remaining | Requests left before throttle |
X-RateLimit-Reset | Unix timestamp when window resets |
Retry-After | Seconds 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.
/v1/verify{
"email": "[email protected]"
}| Field | Type | Required | Description |
|---|---|---|---|
email | string | required | The email address to verify |
200 โ Result ready
| Field | Type | Description |
|---|---|---|
job_id | uuid | Unique ID for this verification job |
email | string | Echo of the verified address |
result | string | validMailbox 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_all | boolean | Domain accepts any local-part |
is_disposable | boolean | Known disposable / temporary domain |
is_role | boolean | Role-based address (info@, support@โฆ) |
is_free_email | boolean | Free / consumer email provider (Gmail, Yahoo, etc.) |
did_you_mean | string | null | Suggested correction for common domain typos, e.g. gmal.com โ gmail.com |
mx_host | string | null | Highest-priority MX record for the domain |
smtp_code | integer | null | SMTP RCPT TO response code |
checked_at | ISO 8601 | When the check completed |
server_region | string | Always GB |
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.
/v1/verify/bulk{
"emails": [
"[email protected]",
"[email protected]",
"[email protected]"
],
"webhook_url": "https://yourapp.co.uk/webhooks/mailverify"
}| Field | Type | Required | Description |
|---|---|---|---|
emails | string[] | required | 1 โ 5,000 email addresses |
webhook_url | string | optional | HTTPS URL to POST results to when the batch completes |
202 โ Accepted
{
"batch_id": "9a1c2d3e-...",
"total": 3,
"queued": 3,
"estimated_completion_seconds": 9
}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.
/v1/results/:jobIdResponse
{
"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"
}| status | Meaning |
|---|---|
| pending | Job is queued or actively being checked |
| complete | Verification finished โ result field is populated |
| failed | All 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).
/v1/results/batch/:batchId| Query param | Default | Description |
|---|---|---|
page | 1 | Page number (1-indexed) |
limit | 100 | Results 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.
/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).
/v1/webhooks{
"url": "https://yourapp.co.uk/webhooks/mailverify",
"events": ["batch.complete"]
}| Field | Type | Required | Description |
|---|---|---|---|
url | string | required | HTTPS URL to receive events |
events | string[] | optional | Defaults to ["batch.complete"]. Also accepts "job.complete", "job.failed" |
/v1/webhooks/:idRemove 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"
}| HTTP | code | Meaning |
|---|---|---|
| 401 | INVALID_API_KEY | Missing, malformed, or revoked API key |
| 402 | INSUFFICIENT_CREDITS | No credits remaining or monthly limit reached |
| 422 | INVALID_EMAIL | Email address fails syntax validation |
| 422 | BATCH_TOO_LARGE | Batch exceeds 5,000 email limit |
| 429 | RATE_LIMITED | Too many requests โ check Retry-After |
| 404 | JOB_NOT_FOUND | Job or batch ID not found (or belongs to a different key) |
| 500 | INTERNAL_ERROR | Unexpected server error โ our team is automatically alerted |
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.