# Laravel Email Validation Rule

URL: https://email-validation.org/docs/guides/laravel/
Updated: 2026-11-10

> Documentation index: https://email-validation.org/llms.txt

A Laravel validation rule that fails an email address that cannot receive email, in any form request or
`Validator::make` call. It is built on the official [PHP client library](https://email-validation.org/docs/sdks/php/), checks each address once, and never
stops a real person from signing up when the API cannot answer in time.

```php
use EmailValidation\Laravel\ValidEmail;

$request->validate([
    'email' => ['bail', 'required', 'email', new ValidEmail()],
]);
```

## Install

The package is not on Packagist yet. Download it here and install it from a folder of your app:

1. Download [email-validation-laravel-0.1.0.zip](https://email-validation.org/downloads/laravel/email-validation-laravel-0.1.0.zip) and save it in a folder of your project, for example `packages/`.
2. Add that folder as a Composer repository and require the package:

   ```bash
   composer config repositories.email-validation-laravel artifact ./packages
   composer require email-validation/laravel:^0.1
   ```

   Composer installs the PHP client library from Packagist. Laravel discovers the service provider by itself.
3. Create an API key on the [API keys page](https://email-validation.org/app/api-keys/) with the permission "Validate addresses", and give it a
   [daily credit cap](https://email-validation.org/docs/authentication/#limits) to limit what a busy form can spend, and put the form's route
   behind Laravel's `throttle` middleware: each new address a visitor sends is a paid check. Put the key in `.env`:

   ```bash
   EV_API_KEY=your-api-key
   ```

   The key is read from your configuration only; never put it in code.

It needs Laravel 10 or newer and PHP 8.1 or newer.

## Use the rule

Add the rule object, or the string rule, after Laravel's own `email` rule. With `bail`, a malformed address stops at
`email` and is never sent.

```php
'email' => ['bail', 'required', 'email', new ValidEmail()],

// fail disposable addresses too, and refuse the form when the check cannot finish
'email' => ['bail', 'required', 'email', new ValidEmail(failOn: ['invalid', 'disposable'], failOpen: false)],

// the string rule: the project's settings, or the failing statuses as parameters
'email' => 'bail|required|email|valid_email',
'email' => 'bail|required|email|valid_email:invalid,disposable',
```

## Settings

Publish the settings to `config/email-validation.php` to change them:

```bash
php artisan vendor:publish --tag=email-validation-config
```

```php
'api_key' => env('EV_API_KEY'),
'base_url' => env('EV_BASE_URL', 'https://api.email-validation.org'),
'fail_on' => ['invalid'],
'block_disposable' => (bool) env('EV_BLOCK_DISPOSABLE', false),
'fail_open' => (bool) env('EV_FAIL_OPEN', true),
'timeout' => 5,
'client_timeout' => 8,
'cache_store' => env('EV_CACHE_STORE'),
'cache_ttl' => 3600,
```

- **Which statuses fail.** Only `invalid` by default. Set `EV_BLOCK_DISPOSABLE=true` to fail `disposable` addresses
  as well. `catch_all` and `unknown` pass unless you add them: see [what each status means](https://email-validation.org/docs/statuses/).
- **When the check cannot finish** (it timed out, the API could not be reached, it answered with a 5xx or 429 error,
  the account has no credits or no key is set), the address passes and a warning goes to your log with the error code
  and request id, never the address. Set `EV_FAIL_OPEN=false` to fail the field with "We could not check the email
  right now" instead.
- **EU data residency.** Set `EV_BASE_URL=https://api-eu.email-validation.org`; the same key works on both.

## Speed and caching

The rule gives the API 5 seconds for the check (the API takes 5 to 15) and waits 8
seconds for the answer, without retrying: a slow mail server then answers `unknown`, which passes and is free.

Each result is kept in your cache for an hour, under a SHA-256 hash of the trimmed, lower-cased address, so a form
sent again after fixing another field is not checked or charged again. Only the status is kept, never the address,
and `unknown` results are not kept.

## Messages

The messages are in the translation namespace `email-validation`. Publish them to change or translate them:

```bash
php artisan vendor:publish --tag=email-validation-lang
```

A message for one form, such as `'email.valid_email' => 'Please use another address.'`, also works with the string
rule. The messages never show the API's own error text.

## Costs

Each completed check costs one credit. Results with the status `unknown` are free, and so are malformed addresses
and the [test addresses](https://email-validation.org/docs/test-addresses/): try the rule with `accepted@sandbox.email-validation.org` (passes) and
`no_mailbox@sandbox.email-validation.org` (fails) at no cost. See [credits and billing](https://email-validation.org/docs/credits-and-billing/).

## Related

- [Validating sign-up forms](https://email-validation.org/docs/guides/signup-form-validation/): what to block and what to accept
- [The PHP client library](https://email-validation.org/docs/sdks/php/) for other calls, such as bulk lists
