mailgun/mailgun-php

The Mailgun SDK provides methods for all API functions.

Maintainers

Package info

github.com/mailgun/mailgun-php

pkg:composer/mailgun/mailgun-php

Statistics

Installs: 29 570 824

Dependents: 194

Suggesters: 13

Stars: 1 135

Open Issues: 2


README

The official Mailgun PHP SDK — a clean, PSR-18 HTTP client wrapper around the Mailgun API.

Latest Version Total Downloads License Join the chat at https://gitter.im/mailgun/mailgun-php

Table of Contents

Requirements

  • PHP 7.4 or higher
  • A PSR-18 HTTP client (e.g. symfony/http-client, guzzlehttp/guzzle)
  • A PSR-7 / PSR-17 implementation (e.g. nyholm/psr7)

The SDK is not coupled to any specific HTTP library — bring your own PSR-18 client.

Installation

composer require mailgun/mailgun-php symfony/http-client nyholm/psr7

EU region? Use https://api.eu.mailgun.net as your endpoint (see below).

Quick Start

require 'vendor/autoload.php';

use Mailgun\Mailgun;

// US servers (default)
$mg = Mailgun::create('your-api-key');

// EU servers
$mg = Mailgun::create('your-api-key', 'https://api.eu.mailgun.net');

Note: The $domain you pass to any API call must match a domain configured in app.mailgun.com.

Sending Email

Simple message

$mg->messages()->send('example.com', [
    'from'    => 'Alice <alice@example.com>',
    'to'      => 'bob@example.com',
    'subject' => 'Hello from Mailgun!',
    'text'    => 'This is a plain-text body.',
    'html'    => '<p>This is an <strong>HTML</strong> body.</p>',
]);

With attachments and tracking options

$mg->messages()->send('example.com', [
    'from'       => 'alice@example.com',
    'to'         => ['bob@example.com', 'carol@example.com'],
    'subject'    => 'Monthly report',
    'text'       => 'Please find the report attached.',
    'attachment' => [['filePath' => '/tmp/report.pdf', 'filename' => 'report.pdf']],
    'o:tracking' => 'yes',
    'o:tag'      => ['monthly', 'report'],
]);

Scheduled delivery

$mg->messages()->send('example.com', [
    'from'           => 'alice@example.com',
    'to'             => 'bob@example.com',
    'subject'        => 'See you tomorrow',
    'text'           => 'Scheduled for tomorrow morning.',
    'o:deliverytime' => 'tomorrow 9am UTC',
]);

IP Management

List all account IPs

$response = $mg->ips()->index();

foreach ($response->getItems() as $ip) {
    echo $ip . PHP_EOL;
}

// Dedicated only
$dedicated = $mg->ips()->index(dedicated: true);

// With full details (pool assignments, subaccount ownership, timestamps)
$detailed = $mg->ips()->listIpsDetailed([
    'pool_id'      => 'my-pool-id', // filter by pool ('any' or 'none' also accepted)
    'subaccount_id'=> 'sub-123',
    'limit'        => 25,
]);

foreach ($detailed->getItems() as $ip) {
    echo "{$ip['address']} — pools: " . implode(', ', $ip['pool_ids']) . PHP_EOL;
}

Inspect a single IP

$ip = $mg->ips()->show('1.2.3.4');

echo $ip->getIp();       // "1.2.3.4"
echo $ip->getRdns();     // reverse DNS
var_dump($ip->getDedicated()); // bool

Assign / remove an IP on a specific domain

// Add IP to a domain
$mg->ips()->assign('example.com', '1.2.3.4');

// Remove IP from a domain
$mg->ips()->unassign('example.com', '1.2.3.4');

// List IPs currently assigned to a domain
$response = $mg->ips()->domainIndex('example.com');
print_r($response->getItems());

Bulk IP operations across all domains

// Assign an IP to every domain in the account (async)
$ref = $mg->ips()->assignIpToAllDomains('1.2.3.4');
echo $ref->getReferenceId(); // track the async operation

// Remove an IP from all domains, replacing it with another
$ref = $mg->ips()->removeIpFromAllDomains('1.2.3.4', alternative: '5.6.7.8');
echo $ref->getMessage();

Find all domains using a specific IP

$response = $mg->ips()->domainsByIp('1.2.3.4', limit: 20, search: 'example');

foreach ($response->getItems() as $domain) {
    echo $domain . PHP_EOL;
}

Dedicated IP band

// Move an account IP into a dedicated IP band
$mg->ips()->placeAccountIpToBand('1.2.3.4');

Request a new dedicated IP

// Check how many dedicated IPs your plan allows
$available = $mg->ips()->numberOfIps();

// Provision a new dedicated IP
$mg->ips()->addDedicatedIp();

Dynamic IP Pools (DIPP)

Dynamic IP Pools let you group dedicated IPs and link them to domains, so sending traffic is spread across all IPs in the pool automatically.

List and inspect pools

// All pools in the account
$response = $mg->ips()->listIpPools();

foreach ($response->getIpPools() as $pool) {
    echo "{$pool['pool_id']}{$pool['name']}" . PHP_EOL;
    echo "  IPs: " . implode(', ', $pool['ips']) . PHP_EOL;
    echo "  Linked to domains: " . ($pool['is_linked'] ? 'yes' : 'no') . PHP_EOL;
}

// Single pool details
$pool = $mg->ips()->loadDIPPInformation('my-pool-id');

echo $pool->getPoolId();      // "my-pool-id"
echo $pool->getName();        // "Primary sending pool"
echo $pool->getDescription(); // "Main US sending pool"
print_r($pool->getIps());     // ["1.2.3.4", "5.6.7.8"]
var_dump($pool->isLinked());  // bool — whether domains are attached

Create and configure a pool

// Create a new pool
$mg->ips()->createIpPool('Primary Pool', 'Main US outbound pool');

// Modify pool metadata, add/remove IPs, link/unlink domains — all in one call
$mg->ips()->updateIpPool('my-pool-id', [
    'name'          => 'Primary Pool v2',
    'add_ip'        => '9.10.11.12',
    'remove_ip'     => '1.2.3.4',
    'link_domain'   => 'example.com',
    'unlink_domain' => 'old.example.com',
]);

Manage IPs inside a pool

// Add a single IP to a pool
$mg->ips()->addIpToPool('my-pool-id', '9.10.11.12');

// Add multiple IPs at once
$mg->ips()->addIpsToPool('my-pool-id', ['9.10.11.12', '13.14.15.16']);

// Remove an IP from a pool
$mg->ips()->removeIpFromPool('my-pool-id', '1.2.3.4');

When a pool is linked to domains, adding or removing IPs propagates to all linked domains asynchronously after the API responds.

List domains linked to a pool

$response = $mg->ips()->getIpPoolDomains('my-pool-id', limit: 20);

foreach ($response->getDomains() as $domain) {
    echo $domain['name'] . PHP_EOL;
}

// Paginate using the cursor from the previous response
if ($response->getNextPage()) {
    $next = $mg->ips()->getIpPoolDomains('my-pool-id', page: $response->getNextPage());
}

Delete a pool

// Delete without replacement (pool must not be linked to any domains)
$mg->ips()->deleteDIPP('my-pool-id');

// Replace linked domains with a specific IP before deleting
$mg->ips()->deleteDIPP('my-pool-id', replacementIp: '1.2.3.4');

// Replace linked domains with another pool before deleting
$mg->ips()->deleteDIPP('my-pool-id', replacementPoolId: 'backup-pool-id');

Delegate a pool to a subaccount

// Grant a subaccount access to a pool
$mg->ips()->delegateIpPool('my-pool-id', 'sub-account-id');

// Revoke subaccount access
$mg->ips()->revokeDelegatedIpPool('my-pool-id', 'sub-account-id');

Analytics

$result = $mg->metrics()->loadMetrics([
    'start'      => 'Sun, 22 Dec 2024 00:00:00 +0000',
    'end'        => 'Sun, 29 Dec 2024 00:00:00 +0000',
    'resolution' => 'day',
    'dimensions' => ['time'],
    'metrics'    => ['accepted_count', 'delivered_count', 'clicked_rate', 'opened_rate'],
    'include_aggregates'  => true,
    'include_subaccounts' => true,
]);

foreach ($result->getItems() as $item) {
    echo $item['dimensions']['time'] . ': ' . $item['metrics']['delivered_count'] . ' delivered' . PHP_EOL;
}

Subaccounts

// Create a subaccount
$mg->subaccounts()->create('Marketing Team');

// List all subaccounts
$items = $mg->subaccounts()->index();
print_r($items->getItems());

// Enable / disable
$mg->subaccounts()->enable($subAccountId);
$mg->subaccounts()->disable($subAccountId);

Make API calls on behalf of a subaccount

// Pass the subaccount ID as the third argument to Mailgun::create()
$mg = Mailgun::create('your-api-key', 'https://api.mailgun.net', $subAccountId);

// All subsequent calls are scoped to that subaccount
$mg->messages()->send('example.com', [...]);

Response Handling

All API methods return typed model objects with IDE-friendly getters by default.

$domain = $mg->domains()->show('example.com');

foreach ($domain->getInboundDNSRecords() as $record) {
    echo $record->getType() . ': ' . $record->getValue() . PHP_EOL;
}

Array responses

Prefer raw arrays? Inject ArrayHydrator:

use Mailgun\Hydrator\ArrayHydrator;
use Mailgun\HttpClient\HttpClientConfigurator;

$configurator = new HttpClientConfigurator();
$configurator->setApiKey('your-api-key');

$mg = new Mailgun($configurator, new ArrayHydrator());

$data = $mg->domains()->show('example.com');
// $data is now a plain associative array

Raw PSR-7 response

Need the raw response? Use NoopHydratornote: no exceptions are thrown on non-200 responses when using this hydrator.

use Mailgun\Hydrator\NoopHydrator;

$mg = new Mailgun($configurator, new NoopHydrator());
$response = $mg->messages()->send('example.com', [...]);
// $response is a Psr\Http\Message\ResponseInterface
echo $response->getStatusCode();

Debugging

Route traffic through Mailgun's Postbin to inspect what the SDK sends:

use Mailgun\HttpClient\HttpClientConfigurator;
use Mailgun\Hydrator\NoopHydrator;

$configurator = new HttpClientConfigurator();
$configurator->setEndpoint('http://bin.mailgun.net/aecf68de'); // replace with your bin ID
$configurator->setApiKey('your-api-key');
$configurator->setDebug(true);

$mg = new Mailgun($configurator, new NoopHydrator());

$mg->messages()->send('example.com', [
    'from'    => 'alice@example.com',
    'to'      => 'bob@example.com',
    'subject' => 'Debug test',
    'text'    => 'Checking what hits the wire.',
]);

Custom HTTP requests

$client = $mg->httpClient();

$client->httpGet('/v3/domains', ['limit' => 5]);
$client->httpPost('/v3/some/path', ['key' => 'value']);
$client->httpPut('/v3/some/path', ['key' => 'value']);
$client->httpDelete('/v3/some/path');

Framework Integration

Framework Package
Symfony tehplague/swiftmailer-mailgun-bundle
Yii2 katanyoo/yii2-mailgun-mailer
CakePHP narendravaghela/cakephp-mailgun
Drupal drupal/mailgun
Laravel Built-in — see Laravel Mail docs

Contributing

This is an open-source project under the MIT license, maintained by Mailgun and the community.

Running the tests

git clone git@github.com:mailgun/mailgun-php.git
cd mailgun-php
composer install
composer test

Ways to help

  • Test the dev-master branch and open issues for anything broken
  • Review open pull requests
  • Add tests for untested endpoints
  • Improve documentation and examples

Support