Send with your stack

Send emails with PHP

Send email from PHP with the official mailblastr/mailblastr Composer package — no runtime dependencies beyond the curl and json extensions.

The official `mailblastr/mailblastr` package is the fastest way to send email from PHP. It has no Composer runtime dependencies — just the built-in curl and json extensions — and is built around a single factory: Mailblastr::client($apiKey) returns a client that exposes one property per API resource.

The package also sets the required User-Agent header on every request for you (the API answers a request without one with a 403 validation_error), automatically retries 429 and 503, and throws a typed exception instead of handing back raw HTTP.

Prerequisites

  1. A verified domain for your from address. (guide)
  2. An API key (mb_...), stored in your environment as MAILBLASTR_API_KEY. (Authentication)
  3. PHP 8.1 or newer with the curl and json extensions enabled — the package declares "php": "^8.1", ext-curl and ext-json.
  4. Composer to install the package.

Install

composer require mailblastr/mailblastr

The current release is 5.0.0. The package autoloads under the Mailblastr\ PSR-4 namespace.

Create the client

Call the static Mailblastr::client() factory with your key. Read the key from the environment so the secret never lands in source control.

mailer.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Mailblastr\Mailblastr;

$mailblastr = Mailblastr::client(getenv('MAILBLASTR_API_KEY'));

A second array argument tunes the client. timeout (per-request seconds, default 30; 0 disables it) and maxRetries (default 2, 0 disables) configure the default curl transport; baseUrl overrides the API host, and transport swaps in any Mailblastr\Transport\TransportInterface implementation — useful for tests. Only 429 and 503 are ever retried, so a retry can never duplicate a send.

$mailblastr = Mailblastr::client(getenv('MAILBLASTR_API_KEY'), [
    'timeout' => 30,     // seconds per request (0 = no timeout)
    'maxRetries' => 2,   // automatic retries on 429/503 only (0 disables)
]);

Send your first email

Every field of the send body is a plain array key — from is a reserved word in PHP, so the payload is an associative array rather than named arguments. to takes a list of up to 50 recipients.

send.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Mailblastr\Mailblastr;

$mailblastr = Mailblastr::client(getenv('MAILBLASTR_API_KEY'));

$sent = $mailblastr->emails->send([
    'from' => 'Acme <hello@yourdomain.com>',
    'to' => ['delivered@mailblastr.dev'],
    'subject' => 'Hello from PHP',
    'html' => '<p>Your first email 🐘</p>',
]);

echo "Sent email {$sent['id']}\n";
delivered@mailblastr.dev is the MailBlastr simulator: the send is accepted, recorded, and marked delivered without touching a real inbox. Swap it for your own address once the first call succeeds — and never use @example.com, which the API rejects with 422.

Handling the response

Every method returns the decoded JSON response as an associative array, so a successful send gives you $sent['id']. Any non-2xx answer throws Mailblastr\Exceptions\MailblastrException, which carries the { statusCode, name, message } envelope. Branch on getName() together with getStatusCode(), never on the message — messages are scrubbed server-side and are not a stable contract.

use Mailblastr\Exceptions\MailblastrException;

try {
    $sent = $mailblastr->emails->send($payload);
    echo "Sent email {$sent['id']}\n";
} catch (MailblastrException $e) {
    echo $e->getStatusCode();   // 422
    echo $e->getName();         // 'validation_error'
    echo $e->getMessage();      // human text — do not match on it

    // WHICH quota ran out, and what would clear it (null on other errors).
    if ($limit = $e->getLimit()) {
        echo "{$limit['kind']}: {$limit['used']}/{$limit['limit']} used";
    }

    // Reputation gates: whether waiting helps, and until when.
    if ($rep = $e->getReputation()) {
        echo $rep['retryable'] ? "retry at {$rep['retry_at']}" : 'not retryable';
    }

    print_r($e->getBody());      // the whole parsed error body
}

A transport-level failure throws the same exception with getStatusCode() === 0 and a getName() of network_error, so one catch clause covers both.

Retry safely with an idempotency key

Pass ['idempotencyKey' => '...'] as the second argument so replaying a request returns the original response instead of sending twice. Only emails->send() and batch->send() honour it — see Idempotency keys.

$mailblastr->emails->send($payload, ['idempotencyKey' => 'order-123']);

// Up to 100 emails in one request (batch items reject attachments and scheduled_at).
$mailblastr->batch->send([
    ['from' => 'hello@yourdomain.com', 'to' => ['delivered@mailblastr.dev'], 'subject' => 'Hi A', 'html' => '<p>A</p>'],
    ['from' => 'hello@yourdomain.com', 'to' => ['delivered@mailblastr.dev'], 'subject' => 'Hi B', 'html' => '<p>B</p>'],
], ['idempotencyKey' => 'orders-2026-08-19']);

If a batch fails part way through, the exception names the emails that already went out — read $e->getSent() and $e->getSentCount() and do not resend those.

Schedule instead of sending now

Add scheduled_at (ISO 8601, at most 30 days ahead) to hand the send to the scheduler. A scheduled send goes through the real delivery path, so it cannot target the simulator address — use one of your own recipients.

$scheduled = $mailblastr->emails->send([
    'from' => 'Acme <hello@yourdomain.com>',
    'to' => ['you@yourdomain.com'],
    'subject' => 'Tomorrow morning',
    'html' => '<p>Queued ahead of time.</p>',
    'scheduled_at' => '2026-09-01T09:00:00Z',
]);

$mailblastr->emails->update($scheduled['id'], ['scheduled_at' => '2026-09-01T14:00:00Z']); // reschedule
$mailblastr->emails->cancel($scheduled['id']);                                             // or call it off
Keep the mb_ API key server-side — load it from an environment variable or secrets manager, never commit it, and never echo it into a Blade view or any browser response. Anyone holding the key can send email as you.

Next steps