Email verification API
Verify one address or up to 100 at a time over a simple REST API. Every result comes with a plain-language reason, and results we cannot resolve are never charged.
Quick start
- Create a free account (100 free credits).
- Create an API key on the API keys page of your dashboard. Keys start with
key_live_. - Send your first request:
curl -X POST https://api.pickusmail.com/v1/verify \
-H "Authorization: Bearer key_live_…" \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com"}'Authentication
Send your key in the Authorization header as Bearer key_live_… on every request. Keep keys on your server; never put them in browser or mobile code. You can revoke a key at any time from the dashboard.
Base URL: https://api.pickusmail.com
Verify one address
POST /v1/verify
| Body field | Type | Description |
|---|---|---|
email | string | The address to check. Required. |
fresh | boolean | Skip the cache and run a live check. Optional, default false. |
Response:
{
"result": {
"email": "alice@example.com",
"verdict": "valid",
"subStatus": "mailbox_exists",
"reason": "The mail server confirmed this mailbox exists.",
"domain": "example.com",
"isCatchAll": false,
"didYouMean": null,
…
},
"credits": { "charged": 1, "balance": 4999 }
}Verify up to 100 addresses
POST /v1/verify/batch with {"emails": ["a@example.com", "b@example.com"]}
Returns results (one result per address, in the same shape as above), counts by verdict, and credits. For larger lists, upload an Excel or CSV file from the dashboard instead.
const res = await fetch('https://api.pickusmail.com/v1/verify/batch', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PICKUSMAIL_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ emails: ['a@example.com', 'b@example.com'] }),
});
const { results, counts, credits } = await res.json();Check your balance
GET /v1/credits returns balance, consumed30d, your plan, and whether your credits expire.
Verdicts and billing
| Verdict | Meaning | Cost |
|---|---|---|
valid | The mailbox exists and accepts mail. Safe to send. | 1 credit |
invalid | Will bounce. Remove it. | 1 credit |
risky | Catch-all domain, role address, full mailbox and similar. Your decision. | 1 credit |
unknown | We could not reach a conclusion, for example a timeout. Try again later. | Free |
Result fields
| Field | Type | Description |
|---|---|---|
email | string | The address as checked. |
verdict | string | valid, invalid, risky or unknown. |
subStatus | string | The specific reason code (see the table below). |
reason | string | One plain sentence explaining the result, safe to show to users. |
domain | string | The domain part of the address. |
provider | string | null | The mailbox provider when recognised, e.g. Google or Microsoft. |
mxHost | string | null | The mail server that was checked. |
isRole | boolean | A shared address such as info@ or support@. |
isDisposable | boolean | A temporary or throwaway email provider. |
isCatchAll | boolean | The domain accepts mail for any address. |
didYouMean | string | null | A suggested correction for a likely typo, e.g. gmail.com for gmial.com. |
smtpCode | number | null | The mail server’s reply code, when the check reached SMTP. |
smtpMessage | string | null | The mail server’s reply text. |
fromCache | boolean | Served from a recent check of the same address. |
durationMs | number | How long the check took. |
checkedAt | string | When the check ran (ISO 8601). |
Sub-statuses
The reason field always carries the sentence shown here.
| subStatus | Verdict | Reason |
|---|---|---|
mailbox_exists | valid | The mail server confirmed this mailbox exists. |
cached_valid | valid | Verified recently — result served from cache. |
syntax_error | invalid | This is not a validly formed email address. |
no_mx_record | invalid | This domain has no mail server, so it cannot receive email. |
dead_domain | invalid | This domain does not resolve — it is expired or misconfigured. |
mailbox_not_found | invalid | The mail server said this mailbox does not exist. |
disposable_domain | invalid | This is a throwaway address from a temporary-email service. |
typo_domain | invalid | The domain looks like a misspelling of a well-known provider. |
local_part_too_long | invalid | The part before the @ exceeds the 64-character limit. |
domain_too_long | invalid | The domain exceeds the 255-character limit. |
catch_all_domain | risky | This domain accepts any address, so delivery cannot be confirmed. |
role_account | risky | This is a shared team address, not a person. It often has low engagement. |
full_mailbox | risky | The mailbox exists but is full and is rejecting new mail. |
no_reply_address | risky | This address is used for sending only and does not accept replies. |
low_deliverability_domain | risky | This domain has a history of rejecting bulk mail. |
timeout | unknown | The mail server did not respond in time. Re-check this address later. |
greylisted | unknown | The mail server asked us to retry later — a common anti-spam measure. |
probe_blocked | unknown | The mail server declined to answer verification requests. |
connection_refused | unknown | The mail server refused the connection. |
temporary_failure | unknown | The mail server reported a temporary problem on its side. |
antispam_challenge | unknown | An anti-spam system intercepted the check. |
probe_unavailable | unknown | Verification capacity was unavailable. This check was not charged. |
Errors
Errors use the HTTP status and a stable code you can branch on:
{ "error": { "code": "insufficient_credits", "message": "…" } }| HTTP | code | Meaning |
|---|---|---|
| 400 | validation_failed | The request body failed validation. |
| 401 | unauthenticated | Authentication required. |
| 401 | invalid_api_key | That API key is not valid. |
| 401 | api_key_revoked | That API key has been revoked. |
| 402 | insufficient_credits | Not enough credits for this request. |
| 403 | account_suspended | This account is suspended. Contact support. |
| 429 | rate_limited | Too many requests. Slow down and retry. |
| 500 | internal_error | Something went wrong on our side. |
On 429, wait a moment and retry. An unknown verdict is not an error: the request succeeded, nothing was charged, and you can check the address again later.
Need help?
Questions about the API? Contact us. New to email verification? Start with how to reduce your email bounce rate.
Get an API key with 100 free credits. No card required.