1. Home
  2. API docs
  3. Laravel

Validate email addresses in Laravel with a mailbox check

Create a custom Laravel validation rule that calls https://simpleverifier.com/api/v1/verify with the Http client and fails only when the API returns status invalid. Put it after bail, required and a format check, read the key from config/services.php, and let the address pass if the API times out.

By SimpleVerifier Team

Before you start

  • Create a key on the API keys page. Keys start with sv_live_ and are shown once.
  • Add SIMPLEVERIFIER_KEY=sv_live_... to .env and read it through config/services.php, so it still works after php artisan config:cache.
  • API access is included in the Pro plan ($29.99/month) and the Email + Phone plan ($49.99/month). See pricing.

1. Create the rule

Run php artisan make:rule DeliverableEmail and replace the class with this. It uses the ValidationRule interface from Laravel 10 and later.

app/Rules/DeliverableEmail.php
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class DeliverableEmail implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! is_string($value) || filter_var($value, FILTER_VALIDATE_EMAIL) === false) {
            $fail('The :attribute must be a valid email address.');
            return;
        }

        $key = config('services.simpleverifier.key');
        if (! $key) {
            Log::warning('SIMPLEVERIFIER_KEY is not set; skipping the mailbox check.');
            return;
        }

        try {
            $response = Http::withToken($key)
                ->acceptJson()
                ->connectTimeout(5)
                ->timeout(30)
                ->get('https://simpleverifier.com/api/v1/verify', ['email' => $value]);
        } catch (ConnectionException $e) {
            Log::warning('SimpleVerifier unreachable', ['error' => $e->getMessage()]);
            return; // fail open
        }

        if (! $response->ok()) {
            Log::warning('SimpleVerifier returned HTTP '.$response->status());
            return; // 401, 402, 429, 503: fail open
        }

        if ($response->json('status') === 'invalid') {
            $fail('The :attribute cannot receive mail. Please check it.');
        }
    }
}

2. Register the key

Calling env() outside config files returns null once the config is cached, so the rule reads the key through config().

config/services.php
<?php

// config/services.php
return [
    // ...your other services

    'simpleverifier' => [
        'key' => env('SIMPLEVERIFIER_KEY'),
    ],
];

3. Use it in a request

bail stops at the first failing rule, so a missing or malformed address never reaches the API. The same array works in a Form Request's rules() method.

app/Http/Controllers/SignupController.php
<?php

namespace App\Http\Controllers;

use App\Rules\DeliverableEmail;
use Illuminate\Http\Request;

class SignupController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'email' => ['bail', 'required', 'string', 'max:254', new DeliverableEmail],
        ]);

        // $validated['email'] passed the mailbox check (or the API was unavailable).

        return back()->with('status', 'Thanks for signing up!');
    }
}

What the API returns

GET or POST https://simpleverifier.com/api/v1/verify with email runs the syntax, domain, disposable-domain and live mailbox checks in one call and returns:

{
  "email": "jane@company.com",
  "status": "valid",
  "reason": "Mailbox exists",
  "mailbox_checked": true
}

Every snippet above follows the same rule: only a successful response with status set to invalid blocks a person.

ResultWhat it meansWhat to do
validThe mail server confirmed the mailbox exists.Accept.
riskyThe domain is real but the mailbox couldn't be confirmed: catch-all domains, role addresses like info@, providers that hide mailbox status.Accept on signup forms. Store the status and keep these out of cold campaigns.
invalidBad format, no mail server, a disposable domain, or the mailbox doesn't exist.Reject and ask the person to check the address.
timeout, 429 or 503The check didn't finish: a slow mail server, too many calls this minute, or a brief outage.Accept (fail open) and log it. Re-check later if it matters.
401 or 402The key is wrong or revoked, or the subscription isn't active.Accept, and alert yourself: every check is being skipped until it's fixed.

Errors return JSON with an error message. Each key can make 120 calls a minute; for thousands of addresses, upload a CSV in the dashboard instead.

Phone numbers and spreadsheets

The same key works with /api/v1/phone?phone=…&country=… to validate a phone number and normalise it to E.164, with live carrier lookups on the Email + Phone plan. The request and response are in the API reference. To check a column of addresses without code, use the Google Sheets add-on with the same key.

Common questions

How is this different from Laravel's email:dns rule?
email:dns confirms the domain has DNS records that can receive mail. It does not ask the mail server whether the specific mailbox exists, and it does not know which domains hand out disposable addresses. The SimpleVerifier rule covers the domain, disposable domains and the mailbox itself.
Will this slow down my form?
A mailbox check usually finishes quickly, but a slow mail server can take several seconds. The rule waits up to 30 seconds and then lets the address through. On a busy signup form you can lower the timeout, or queue a job that verifies the address after signup and flags the account instead of blocking it.
How much does the SimpleVerifier API cost?
API access is included in the paid plans: Pro at $29.99/month and Email + Phone at $49.99/month. There are no per-call credits. Each key can make 120 calls a minute.
Should I reject risky addresses?
Not on a signup or contact form. Risky means the mailbox couldn't be confirmed, which includes every address at a catch-all company domain. Accept it, store the status, and leave risky addresses out of cold outreach.
What if the API is slow or down?
Let the address through. A mailbox check can take several seconds when a mail server is slow, so cap the wait at about 30 seconds (less on a busy form) and treat a timeout, a 429 or a 503 as unknown rather than invalid.

Ready to clean your email and phone lists?

Start verifying emails and phone numbers today and boost your deliverability.

Get Started

Cancel anytime • No setup required