Quickstart
Create an API key from the dashboard, then call /v1/verify. That is the whole integration.
curl "https://api.veridock.io/v1/verify?email=jane.doe@acme.com" \
-H "Authorization: Bearer vd_live_••••••••••••"Authentication
Every request needs a bearer token in the Authorization header. Keys are prefixed vd_live_ or vd_test_. Test keys return deterministic fixtures and never consume credits, so your CI can run against them.
Authorization: Bearer vd_live_••••••••••••Each key has its own usage, rate limit, and optional spend cap. Never ship a live key to a browser; use a test key or proxy through your server.
Verify an address
GET /v1/verify?email={email}
| Parameter | Type | Description |
|---|---|---|
| string, required | The address to verify. URL-encode it. | |
| smtp | boolean, default true | Set false to skip the live SMTP check for a faster, syntax-and-DNS-only answer. |
| timeout | integer, default 8000 | Maximum milliseconds to wait for the receiving server before returning unknown. |
| cache | boolean, default true | Set false to force a fresh check and bypass the 30-day result cache. |
POST /v1/verify with a JSON body {"email": "…"} is also accepted and behaves identically.
Response fields
{
"email": "jane.doe@acme.com",
"status": "deliverable",
"reason": "accepted_email",
"score": 97,
"checks": {
"syntax": true,
"domain": true,
"mx": true,
"smtp": true,
"disposable": false,
"role_account": false,
"catch_all": false,
"free_provider": false
},
"domain": {
"name": "acme.com",
"mx": ["aspmx.l.google.com"],
"provider": "google_workspace"
},
"suggestion": null,
"billed": true,
"duration_ms": 412
}| Field | Type | Description |
|---|---|---|
| string | The normalized (lower-cased, trimmed) address that was verified. | |
| status | enum | deliverable · undeliverable · risky · unknown. Branch on this. |
| reason | enum | The specific finding behind the status. See the full list below. |
| score | integer | 0–100 confidence that mail will be delivered. Useful for thresholds on risky results. |
| checks.syntax | boolean | RFC 5322 syntax passed. |
| checks.domain | boolean | Domain resolves in DNS. |
| checks.mx | boolean | Domain publishes at least one MX record. |
| checks.smtp | boolean | Receiving server accepted the mailbox during the SMTP conversation. |
| checks.disposable | boolean | Domain is a known temporary or throwaway inbox provider. |
| checks.role_account | boolean | Local part is a role such as info, sales, or support. |
| checks.catch_all | boolean | Domain accepts every address, so mailbox existence cannot be confirmed. |
| checks.free_provider | boolean | Domain is a public webmail provider such as gmail.com. |
| domain.mx | string[] | Up to three MX hosts, lowest priority first. |
| domain.provider | string | null | Detected mail provider, e.g. google_workspace or microsoft_365. |
| suggestion | string | null | A corrected address when a likely typo is detected. |
| billed | boolean | Whether this request consumed a credit. Always false for unknown and for cached results. |
| duration_ms | integer | Server-side processing time. |
Statuses and reasons
Branch your code on status. Use reason when you want finer rules, for example allowing role accounts for B2B signups but rejecting disposable ones.
accepted_emailBilling: one credit, cached 30 days.
invalid_syntaxinvalid_domainno_mx_recordrejected_emailmailbox_fullBilling: one credit, cached 30 days.
disposablerole_accountcatch_alllow_qualitylow_deliverabilityBilling: one credit, cached 30 days.
timeoutgreylistedunavailable_smtpBilling: never charged.
Bulk verification
Send an array of addresses or upload a CSV. Jobs run asynchronously, you can poll or receive a webhook, and results are available as CSV, JSON, or newline-delimited JSON filtered by status.
curl -X POST https://api.veridock.io/v1/bulk \
-H "Authorization: Bearer vd_live_••••••••••••" \
-H "Content-Type: application/json" \
-d '{
"name": "october-newsletter",
"emails": ["a@acme.com", "b@acme.com", "c@example.org"],
"webhook_url": "https://example.com/hooks/veridock"
}'
# → { "id": "job_8f2k1", "status": "queued", "total": 3 }| Job status | Meaning |
|---|---|
| queued | Accepted and waiting for a worker. |
| processing | Running. processed increments as results come in. |
| completed | All rows done. download_url is valid for 7 days. |
| failed | Could not run. The error field explains why. Nothing is billed. |
Webhooks
Pass webhook_url when creating a job and we POST the job object to it on completion. Every delivery is signed with your webhook secret in the X-VeriDock-Signature header (HMAC-SHA256 of the raw body). We retry failed deliveries with exponential backoff for 24 hours.
import crypto from "node:crypto";
export function verifySignature(rawBody: string, header: string, secret: string) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}Errors
Errors return a JSON body of the shape {"error": {"code", "message"}}. Failed requests are never billed.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_request | Missing or malformed email parameter. |
| 401 | unauthorized | Missing, revoked, or malformed API key. |
| 402 | quota_exceeded | Monthly credits and spend cap exhausted. Raise the cap or upgrade. |
| 404 | not_found | Unknown job id. |
| 422 | unprocessable | Bulk payload exceeds plan limits or has no valid rows. |
| 429 | rate_limited | Too many concurrent requests. Honor the Retry-After header. |
| 500 | internal_error | Something broke on our side. Retried requests are idempotent and never double-billed. |
Rate limits
Limits are on concurrent in-flight requests per key, not requests per minute, because SMTP checks vary in duration. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and, on 429, Retry-After.
| Plan | Concurrent requests | Bulk rows per job |
|---|---|---|
| Free | 2 | 1,000 |
| Starter | 5 | 100,000 |
| Growth | 20 | 1,000,000 |
| Scale | 100 | Unlimited |
SDKs and integrations
The API is plain HTTPS and JSON, so any HTTP client works. Official packages wrap it with types and retries.
| Platform | Install | Notes |
|---|---|---|
| Node.js / TypeScript | npm i @veridock/sdk | Typed responses, automatic retries, edge-runtime compatible. |
| Python | pip install veridock | Sync and async clients. |
| PHP | composer require veridock/veridock | PSR-18 compatible. |
| Go | go get github.com/veridock/veridock-go | Context-aware client. |
| Zapier · Make · n8n | Search for VeriDock | Verify on new row, new subscriber, or form submission. |
Something missing? Tell us and we will add it.