EmailCheckerEmailChecker

Documentação · PHP

Validar email em PHP

PHP tem 2 caminhos: cURL nativo (zero dependência, mais verboso) ou Guzzle (padrão moderno, requer composer). A validação é assíncrona: você envia o lote, guarda o id e consulta até ele fechar. Os exemplos abaixo usam Guzzle pela legibilidade.

Instalação (composer)

bash
composer require guzzlehttp/guzzle

Passo 1 · POST /api/v1/batch

Enviar o lote

Endpoint assíncrono. Manda de 1 a 100.000 emails num POST e recebe 201 com o id do lote. O campo name é opcional e serve pra você achar o lote depois.

php
<?php
// emailchecker.php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;

$client = new Client([
    'base_uri' => 'https://app.emailchecker.email/api/v1/',
    'timeout' => 30.0,
    'connect_timeout' => 5.0,
    'http_errors' => false, // tratamos o status na mão
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('EMAILCHECKER_API_KEY'),
        'Content-Type' => 'application/json',
    ],
]);

/** Devolve data[] ou explode com a mensagem real da API. */
function ecUnwrap(ResponseInterface $res): array {
    $body = json_decode((string) $res->getBody(), true, 512, JSON_THROW_ON_ERROR);
    if ($res->getStatusCode() >= 400) {
        $msg = $body['error'] ?? 'erro desconhecido';
        throw new RuntimeException("EmailChecker {$res->getStatusCode()}: {$msg}");
    }
    return $body['data'];
}

/** POST /api/v1/batch — de 1 a 100.000 emails. Devolve o id do lote. */
function submitBatch(Client $client, array $emails, ?string $name = null): string {
    $payload = ['emails' => $emails];
    if ($name !== null) {
        $payload['name'] = $name;
    }
    return ecUnwrap($client->post('batch', ['json' => $payload]))['id'];
}

$batchId = submitBatch($client, ['joao@empresa.com.br', 'contato@outra.com.br'], 'minha-lista');
echo "lote enviado: {$batchId}" . PHP_EOL;

Passo 2 · GET /api/v1/batch/{id}

Consultar o resultado

Não enviamos webhook: o jeito de saber que terminou é consultar o lote até status virar completed. Só aí o array emails vem preenchido, com result, score, reason e os atributos isv_*. O limite é de 300 req/min e 10.000/hora por key — um poll a cada 5 segundos sobra.

php
<?php
// poll.php — não existe webhook: consulte o lote até ele fechar

use GuzzleHttp\Client;

/** GET /api/v1/batch/{id} até status === 'completed'. */
function waitForBatch(Client $client, string $batchId, int $interval = 5, int $timeout = 1800): array {
    $deadline = time() + $timeout;

    while (time() < $deadline) {
        $res = $client->get("batch/{$batchId}");
        if ($res->getStatusCode() === 429) { // 300 req/min e 10.000/hora por key
            sleep(60);
            continue;
        }

        $batch = ecUnwrap($res);
        if ($batch['status'] === 'completed') {
            return $batch;
        }
        if (in_array($batch['status'], ['failed', 'cancelled'], true)) {
            throw new RuntimeException("lote {$batch['status']}");
        }

        sleep($interval); // pending | processing
    }

    throw new RuntimeException('lote não terminou dentro do tempo');
}

$batch = waitForBatch($client, $batchId);
echo "{$batch['size']} emails, finalizado em {$batch['finished_at']}" . PHP_EOL;

// emails[] só vem preenchido com status === 'completed'
foreach ($batch['emails'] as $e) {
    // result: 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
    if ($e['result'] === 'deliverable' && $e['score'] >= 80) {
        // grava no banco, dispara worker, etc.
        echo "{$e['email']} score={$e['score']} ({$e['reason']})" . PHP_EOL;
    }
}

Boas práticas

Pitfalls

Armadilhas específicas de PHP

Erros sutis que só aparecem em produção. Conheça antes de subir o código.

Guzzle lança exceção em 4xx/5xx por padrão

Com http_errors ligado (o default), um 422 vira ClientException e um 503 vira ServerException antes de você ler o corpo — e é justamente no corpo que vem o "error" da API. Passe ["http_errors" => false] e inspecione getStatusCode(), ou capture RequestException pra chegar em $e->getResponse().

Timeout default do Guzzle é 0 (infinito)

Sem a opção "timeout" o Guzzle espera para sempre, e em ambiente compartilhado (cPanel) o max_execution_time do PHP mata o script antes com Fatal error genérico. Defina sempre "timeout" => 30 e "connect_timeout" => 5 no client pra falhar de forma controlada.

json_decode devolve null silencioso

json_decode($body, true) retorna null em corpo vazio ou JSON inválido sem lançar nada; acessar $data["data"] aí gera warning e comportamento errado. Use JSON_THROW_ON_ERROR (PHP 7.3+) ou cheque json_last_error() === JSON_ERROR_NONE antes de confiar no array.

allow_url_fopen e cacert em hosting compartilhado

No cURL nativo, hosts compartilhados às vezes vêm sem CA bundle atualizado e o handshake TLS falha com "SSL certificate problem". Não desligue CURLOPT_SSL_VERIFYPEER — aponte CURLOPT_CAINFO pra um cacert.pem atual; com Guzzle isso é a opção "verify".

Mais linguagens

Exemplos em outras linguagens

Comece agora

Pronto pra parar de mandar email pra endereço morto?

Comece grátis com 500 créditos. Sem cartão, sem compromisso.