Email Verification in PHP
This tutorial covers integrating Mailbeam into any PHP application — not just Laravel. You'll see two approaches: using the Guzzle HTTP client (recommended) and plain cURL (no dependencies).
Prerequisites
- PHP 8.1 or later
- Composer (optional, for Guzzle)
- A Mailbeam API key (sign up free)
Setup
Add your API key to your environment:
# .env or environment config
MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxxOption A — Using Guzzle
composer require guzzlehttp/guzzle<?php
// src/Services/MailbeamService.php
namespace App\Services;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class MailbeamService
{
private Client $client;
public function __construct()
{
$this->client = new Client([
'base_uri' => 'https://api.mailbeam.dev',
'timeout' => 5.0,
'headers' => [
'Authorization' => 'Bearer ' . $_ENV['MAILBEAM_KEY'],
'Content-Type' => 'application/json',
],
]);
}
/**
* Returns ['valid' => bool, 'score' => int, 'reason' => string|null]
* or null on API error (caller should fail open).
*/
public function verify(string $email): ?array
{
try {
$response = $this->client->post('/v1/verify', [
'json' => ['email' => $email],
]);
return json_decode($response->getBody()->getContents(), true);
} catch (RequestException $e) {
error_log('[Mailbeam] verify failed: ' . $e->getMessage());
return null;
}
}
}Option B — Plain cURL (no dependencies)
<?php
function mailbeam_verify(string $email): ?array
{
$apiKey = $_ENV['MAILBEAM_KEY'] ?? '';
$payload = json_encode(['email' => $email]);
$ch = curl_init('https://api.mailbeam.dev/v1/verify');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 5,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
]);
$body = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error || !$body) {
error_log('[Mailbeam] cURL error: ' . $error);
return null; // fail open
}
return json_decode($body, true);
}Using verification in your signup
<?php
// signup.php
$email = trim(strtolower($_POST['email'] ?? ''));
$password = $_POST['password'] ?? '';
if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(422);
echo json_encode(['error' => 'Please enter a valid email address.']);
exit;
}
// Verify with Mailbeam
$service = new MailbeamService();
$result = $service->verify($email);
$valid = $result['valid'] ?? true; // fail open if $result is null
$score = $result['score'] ?? 100;
$reason = $result['reason'] ?? null;
if ($result !== null && (!$valid || $score < 60)) {
$message = match ($reason) {
'disposable_domain' => 'Please use a permanent email address.',
'no_mx_records' => 'This email domain cannot receive mail.',
default => 'Please provide a valid email address.',
};
http_response_code(422);
echo json_encode(['error' => $message, 'code' => $reason]);
exit;
}
// Continue with user creation...
create_user($email, $password);
echo json_encode(['success' => true]);Adding a cache layer
<?php
function verify_with_cache(string $email, MailbeamService $service): ?array
{
$key = 'mailbeam_' . md5(strtolower(trim($email)));
// APCu (single-server)
if (apcu_exists($key)) {
return apcu_fetch($key);
}
$result = $service->verify($email);
if ($result !== null) {
apcu_store($key, $result, 3600); // cache for 1 hour
}
return $result;
}Best practices
- Always fail open (return
nullon error, not['valid' => false]) - Normalize email with
strtolower(trim($email))before verifying and caching - Use
FILTER_VALIDATE_EMAILbefore calling Mailbeam to catch obvious syntax errors cheaply - Set a 5-second timeout on the HTTP request