PHP · Email breach checks
Check email breaches in PHP with cURL
For PHP applications the email breach check usually runs after a registration form is submitted or from a queued Laravel job that rechecks accounts. json_decode with the associative flag turns the response into arrays, so a foreach over $result['breaches'] gives you each record's Title, BreachDate and DataClasses. implode makes the data classes readable for a notification email or an admin screen.
The script below is a self-contained CLI program you can run with php check-email.php. It retries only when the 429 code is rate_limited, using the Retry-After header captured by a cURL header callback. A 400 throws InvalidArgumentException, since the address itself needs fixing, and a 401 points at the API key. Any other status, including quota_exceeded, surfaces as a RuntimeException with the error code.
Steps
- 1
Confirm the cURL extension is enabled with php -m and look for curl in the list.
- 2
Export FINOWL_API_KEY, or set it in your web server or PHP-FPM pool environment.
- 3
Save the example as check-email.php and run php check-email.php alex@example.com.
- 4
Use the Title, BreachDate and DataClasses values in your notification or admin view.
<?php
declare(strict_types=1);
function checkEmail(string $email, int $attempts = 3): array
{
for ($i = 0; $i < $attempts; $i++) {
$retryAfter = 1;
$ch = curl_init('https://api.finowl.co/v1/breaches/email');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('FINOWL_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['email' => $email], JSON_THROW_ON_ERROR),
CURLOPT_HEADERFUNCTION => function ($ch, string $header) use (&$retryAfter): int {
if (stripos($header, 'Retry-After:') === 0) {
$retryAfter = max(1, (int) trim(substr($header, 12)));
}
return strlen($header);
},
]);
$body = curl_exec($ch);
if ($body === false) {
throw new RuntimeException('Network error: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$data = json_decode($body, true) ?? [];
$code = $data['error']['code'] ?? 'unknown_error';
if ($status === 200) {
return $data;
} elseif ($status === 400) {
throw new InvalidArgumentException("Invalid email address: {$code}");
} elseif ($status === 401) {
throw new RuntimeException('Unauthorized: check FINOWL_API_KEY');
} elseif ($status === 429 && $code === 'rate_limited') {
sleep($retryAfter);
continue;
}
throw new RuntimeException("Finowl error {$status}: {$code}");
}
throw new RuntimeException('Still rate limited after several attempts');
}
$result = checkEmail($argv[1] ?? 'alex@example.com');
if (!$result['breached']) {
echo $result['email'] . ' does not appear in any known breach' . PHP_EOL;
}
foreach ($result['breaches'] as $breach) {
$flag = $breach['IsVerified'] ? '' : ' [unverified]';
echo "{$breach['Title']} ({$breach['BreachDate']}){$flag}" . PHP_EOL;
echo ' exposed: ' . implode(', ', $breach['DataClasses']) . PHP_EOL;
}IN PRODUCTION
Production tips
Run the lookup from a queue worker, such as a Laravel job, instead of inside the request that renders the signup page. That keeps form submission fast even when the API is slow.
PHP-FPM workers each make their own requests, so a burst of signups can add up quickly. Throttle the queue to keep the whole application under 60 requests per minute.
Escape breach fields with htmlspecialchars before echoing them into HTML. Treat Description in particular as untrusted text and sanitize it if you choose to display it.
Each lookup costs 1 credit, with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check breached passwords in PHP before hashing
- Predict gender from names in PHP 8 with cURL
- Check email breaches in Python with requests
- Check email breaches in Node.js with fetch
- Check email breaches in TypeScript with typed results
- Check email breaches in Go with net/http
- Email breach checks for fintech signups and logins
- Email breach checks for online stores and shopper logins
- Email breach checks for SaaS logins and admins
Check email breaches in PHP with cURL
Grab an API key, paste the example and run it. 100 free credits every month.