Documentação · 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.
composer require guzzlehttp/guzzlePasso 1 · POST /api/v1/batch
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
// 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}
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
// 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;
}
}Pitfalls
Erros sutis que só aparecem em produção. Conheça antes de subir o código.
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().
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($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.
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
Comece grátis com 500 créditos. Sem cartão, sem compromisso.