Validate email addresses in PHP without sending a message
Create a free accountPlans and credits
PHP has a built-in format check, filter_var with FILTER_VALIDATE_EMAIL. It tells you whether an address is well written, not whether anyone can receive mail at it, and sending a test message to find out is slow and hurts your sender reputation. This guide checks the format with filter_var, then checks the mailbox with the Easy Email Verification API through cURL, without sending anything. The code was tested on PHP 8.4 and every example works with the free sandbox key eev_sandbox_key.
1. Check the format with filter_var
function looks_like_email(string $address): bool
{
return filter_var($address, FILTER_VALIDATE_EMAIL) !== false;
}
looks_like_email('jane@example.com'); // true
looks_like_email('jane.doe@example'); // false: no dot in the domain
looks_like_email('jane@localhost'); // false
filter_var is built in, so you do not need a pattern of your own. Trim the input first: filter_var rejects addresses with leading or trailing spaces.
Why format validation is not enough
All of these pass filter_var and still bounce:
| Address | Passes filter_var | Problem |
|---|---|---|
jane@gmial.com | yes | The domain is a typo and does not exist |
former.employee@example.com | yes | The mailbox was closed |
jane@example-site.com | yes | The domain has no MX record, so it receives no mail |
x7k2@temp-inbox.example | yes | Disposable inbox that disappears in a few minutes |
PHP can look up the MX record (getmxrr), but it cannot tell whether the mailbox exists. That takes an SMTP conversation with the mail server, which shared hosting and cloud providers often block on port 25, and which mail servers treat as abuse when it comes from unknown hosts. An email verification API runs those checks for you, and no email is sent to the person.
2. Verify the mailbox with the API
The API key goes in the X-API-Key header and stays on the server, in the EEV_API_KEY environment variable. Without it, the code uses the sandbox key.
const EEV_API_URL = 'https://api.easyemailverification.com/v1/verify';
function eev_api_key(): string
{
return getenv('EEV_API_KEY') ?: 'eev_sandbox_key';
}
function eev_request(string $url, ?array $body = null, int $timeout = 30): array
{
$headers = ['X-API-Key: ' . eev_api_key()];
$ch = curl_init($url);
if ($body !== null) {
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => $timeout,
]);
$raw = curl_exec($ch);
if ($raw === false) {
throw new RuntimeException('EEV request failed: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$data = json_decode($raw, true);
if ($status !== 200) {
throw new RuntimeException('EEV error ' . $status . ': ' . ($data['message'] ?? 'unknown error'));
}
return $data;
}
function verify_email(string $address): array
{
return eev_request(EEV_API_URL . '?email=' . rawurlencode($address));
}
print_r(verify_email('valid@sandbox.easyemailverification.com'));
Use rawurlencode, so that a + in jane+news@example.com is sent as %2B and not read as a space. The response for that sandbox address:
{
"email": "valid@sandbox.easyemailverification.com",
"result": "valid",
"reason": "accepted_email",
"disposable": false,
"accept_all": false,
"role": false,
"free": false,
"user": "valid",
"domain": "sandbox.easyemailverification.com",
"mx_record": "mx.sandbox.easyemailverification.com",
"mx_domain": "easyemailverification.com",
"safe_to_send": true,
"did_you_mean": "",
"success": true,
"message": "Sandbox response: no credits used",
"http_code": "200"
}
Errors throw with the HTTP status and the API message: 401 for an unknown key, 402 when the account has no credits left.
3. Decide what to do with the result
function decide(array $result): string
{
if ($result['did_you_mean'] !== '') {
return 'suggest'; // ask the user: did you mean ...?
}
if ($result['result'] === 'valid' && $result['safe_to_send']) {
return 'accept';
}
if ($result['result'] === 'invalid') {
return 'reject';
}
return 'review'; // unknown, catch-all or disposable: your policy decides
}
- accept: the mail server accepted the mailbox and no risk signal advises against it.
- reject: the address cannot receive mail. The reason says why (
rejected_email,invalid_domain,no_mx_record…). - suggest:
did_you_meanhas a correction, for exampletypo@gmail.comfortypo@gmial.com. Show it in the form. - review: an unknown result, a catch-all domain or a disposable address. Unknown is not invalid: accept the signup and confirm the address by email, or verify it again later.
In a form handler, check the format first and call the API only for addresses that pass, so that you do not spend credits on obvious mistakes. If the API call throws, accept the address and confirm it by email rather than blocking the signup.
4. Verify up to 50 addresses in one request
function verify_emails(array $addresses): array
{
return eev_request(EEV_API_URL, ['emails' => array_values($addresses)], 120); // up to 50 per request
}
foreach (verify_emails(['valid@sandbox.easyemailverification.com', 'typo@gmial.com']) as $r) {
echo $r['email'], ' ', decide($r), "\n";
}
For whole files with thousands of addresses, use the bulk endpoints or the dashboard upload: see bulk email verification and the API reference. On Mautic, the Easy Email Verification plugin does this without code.
Test addresses
With the key eev_sandbox_key, these addresses return fixed answers and use no credits:
| Address | Answer |
|---|---|
valid@sandbox.easyemailverification.com | valid, accepted_email |
invalid@sandbox.easyemailverification.com | invalid, rejected_email |
unknown@sandbox.easyemailverification.com | unknown, timeout |
catchall@sandbox.easyemailverification.com | valid, accept_all: true, safe_to_send: false |
disposable@sandbox.easyemailverification.com | valid, disposable: true, safe_to_send: false |
typo@gmial.com | invalid, did_you_mean: typo@gmail.com |
quota@sandbox.easyemailverification.com | HTTP 402 (no credits) |
When it works, create a free account, generate a key under API settings and set it in EEV_API_KEY. Each verified address uses one credit; the free plan includes 50 verifications a day.
Next steps
- Prefer a library? The official package wraps everything above (single, batch, bulk, credits, sandbox):
composer require easyemailverification/php-sdk(Packagist). - Email Verification API: plans, limits and the other endpoints.
- Result codes: every
result,reasonand risk signal. - Examples in other languages on GitHub.
More ways to verify emails
- Email validation in Node.js: from regex to mailbox checks
- How to validate an email address in Python
- Email validation in Java with Spring Boot
- What every result code means
- API reference
- Check a single address for free
- All integrations
Using an AI assistant? It can follow this guide with you: give it validate-email-php.md.