Mailbeam
PHP 8.1+Beginner15 minutesUpdated January 2025

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_xxxxxxxxxxxxxxxxxxxx

Option 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 null on error, not ['valid' => false])
  • Normalize email with strtolower(trim($email)) before verifying and caching
  • Use FILTER_VALIDATE_EMAIL before calling Mailbeam to catch obvious syntax errors cheaply
  • Set a 5-second timeout on the HTTP request

Next steps