Framework · Laravel
Use Finowl in Laravel with the Http facade
Laravel gives each Finowl product a natural home. Breached password checks become a validation rule you add to a FormRequest next to min and confirmed, email breach checks run in a queued listener on the Registered event, and gender enrichment fits a queued job that processes customers in chunks. The Http facade handles JSON, bearer tokens and timeouts, and Http::fake makes the whole integration simple to test without network access.
Instead of a separate SDK class, the setup below registers an Http::finowl() macro in AppServiceProvider. It reads the key through config('services.finowl.key'), which config/services.php maps to the FINOWL_API_KEY environment variable, so config caching keeps working. A FinowlException::fromResponse helper turns failed responses into the status, error code and Retry-After seconds that rules, listeners and jobs use to decide whether to release, fail or carry on.
STEP 1
A reusable Laravel client
Keep your key in the FINOWL_API_KEY environment variable and route every call through one helper, so authentication, timeouts and error handling live in one place.
php artisan make:rule NotBreachedPassword
php artisan make:listener CheckEmailBreaches --event="Illuminate\Auth\Events\Registered"
php artisan make:job EnrichCustomerGender<?php
// config/services.php
// 'finowl' => ['key' => env('FINOWL_API_KEY')],
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Http::macro('finowl', fn (): PendingRequest => Http::baseUrl('https://api.finowl.co')
->withToken(config('services.finowl.key'))
->acceptJson()
->asJson()
->connectTimeout(5)
->timeout(15));
}
}
// app/Exceptions/FinowlException.php
namespace App\Exceptions;
use Illuminate\Http\Client\Response;
use RuntimeException;
class FinowlException extends RuntimeException
{
public function __construct(
public readonly int $status,
public readonly string $errorCode,
public readonly ?int $retryAfter = null,
) {
parent::__construct("Finowl API error {$status}: {$errorCode}", $status);
}
public static function fromResponse(Response $response): self
{
$retryAfter = $response->header('Retry-After');
return new self(
$response->status(),
$response->json('error.code') ?? 'unknown_error',
$retryAfter !== '' ? (int) $retryAfter : null,
);
}
}STEP 2
Call the API you need

Check email breaches in Laravel with a queued listener
Laravel fires Illuminate\Auth\Events\Registered when a user signs up, both in the starter kits and in controllers that call event(new Registered($user)). The CheckEmailBreaches listener implements ShouldQueue, so the Finowl lookup runs on a queue worker instead of slowing the registration request. It filters breaches to those whose DataClasses include Passwords and notifies the user with each breach Title and BreachDate.

Validate breached passwords with a Laravel rule
NotBreachedPassword implements Laravel's ValidationRule interface, so you use it like any other rule object in a FormRequest or Validator::make call. The validate method sends the value to /v1/breaches/password through the Http::finowl() macro with a five second timeout, and calls $fail when the response says breached is true. The message uses the :attribute placeholder, so it reads naturally whatever the field is called.

Queue gender enrichment jobs in Laravel
EnrichCustomerGender is a queued job that takes up to 50 customer ids, loads the models, and sends them to /v1/gender/batch as name items with an optional country_code hint. array_filter removes the country key when a customer has none, keeping each item valid. Results come back in order with each item's id, so the job updates the matching Customer through the keyed collection without another query.
A FEW GOOD QUESTIONS
Laravel questions
Other languages and frameworks
Ship with Laravel today
100 free credits every month. Try every request in the sandbox first.