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:

AddressPasses filter_varProblem
jane@gmial.comyesThe domain is a typo and does not exist
former.employee@example.comyesThe mailbox was closed
jane@example-site.comyesThe domain has no MX record, so it receives no mail
x7k2@temp-inbox.exampleyesDisposable 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
}

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:

AddressAnswer
valid@sandbox.easyemailverification.comvalid, accepted_email
invalid@sandbox.easyemailverification.cominvalid, rejected_email
unknown@sandbox.easyemailverification.comunknown, timeout
catchall@sandbox.easyemailverification.comvalid, accept_all: true, safe_to_send: false
disposable@sandbox.easyemailverification.comvalid, disposable: true, safe_to_send: false
typo@gmial.cominvalid, did_you_mean: typo@gmail.com
quota@sandbox.easyemailverification.comHTTP 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

By using this website, you automatically accept that we use cookies.