Bounces

Mailgun automatically handles bounced emails. The bounced addresses are collected in a bounces list and subsequent delivery attempts are ignored to protect your sending reputation.

Mailgun can notify your application every time a message bounces.

The list of bounced addresses can be accessed programmatically:

GET /<domain>/bounces

Fetches the list of bounces.

Parameter Description
limit Maximum number of records to return. (100 by default)
skip Number of records to skip. (0 by default)
GET /<domain>/bounces/<address>

Fetches a single bounce event by a given email address. This is useful to check if a given email address has bounced before.

POST /<domain>/bounces

Adds a permanent bounce to the bounces table. Updates the existing record if already here.

Parameter Description
address Valid email address
code Error code (default 550)
error Error description, (default is empty)
DELETE /<domain>/bounces/<address>

“Clears” a given bounce event.

Examples

Fetch the full list of all recipient addresses that bounced:

curl -s --user 'api:YOUR_API_KEY' -G \
   https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces
import com.mailgun.api.v3.suppression.MailgunSuppressionBouncesApi;
import com.mailgun.model.suppression.bounces.BouncesResponse;

// ...

public BouncesResponse getBounces() {
    MailgunSuppressionBouncesApi suppressionBouncesApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionBouncesApi.class);

    return suppressionBouncesApi.getBounces(YOUR_DOMAIN_NAME);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;

# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain   = 'YOUR_DOMAIN_NAME';

# Issue the call to the client.
$result = $mgClient->suppressions()->bounces()->index($domain);
def get_bounces():
    return requests.get(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces",
        auth=("api", "YOUR_API_KEY"))
def get_bounces
  RestClient.get "https://api:YOUR_API_KEY"\
  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces"
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class GetBouncesChunk
{

    public static void Main (string[] args)
    {
        Console.WriteLine (GetBounces ().Content.ToString ());
    }

    public static IRestResponse GetBounces ()
    {
        RestClient client = new RestClient ();
        client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
        client.Authenticator =
            new HttpBasicAuthenticator ("api",
                                        "YOUR_API_KEY");
        RestRequest request = new RestRequest ();
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.Resource = "{domain}/bounces";
        return client.Execute (request);
    }

}
import (
    "context"
    "github.com/mailgun/mailgun-go/v3"
    "time"
)

func ListBounces(domain, apiKey string) ([]mailgun.Bounce, error) {
    mg := mailgun.NewMailgun(domain, apiKey)
    it := mg.ListBounces(nil)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
    defer cancel()

    var page, result []mailgun.Bounce
    for it.Next(ctx, &page) {
        result = append(result, page...)
    }

    if it.Err() != nil {
        return nil, it.Err()
    }
    return result, nil
}
const DOMAIN = 'YOUR_DOMAIN_NAME';

const formData = require('form-data');
const Mailgun = require('mailgun.js');

const mailgun = new Mailgun(formData);

const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
  try {
    const bounces = await client.suppressions.list(DOMAIN, 'bounces');
    console.log('bounces', bounces);
  } catch (error) {
    console.error(error);
  }
})();

Sample JSON response is shown below. Notice the following:

  • There was only one bounce
  • Both SMTP error code and SMTP error message are preserved
{
  "total_count": 1,
  "items": [
      {
          "created_at": "Fri, 21 Oct 2011 11:02:55 GMT",
          "code": 550,
          "address": "'baz@example.com",
          "error": "Message was not accepted -- invalid mailbox.  Local mailbox 'baz@example.com is unavailable: user not found"
      }
  ]
}

Lets check if any messages sent to foo@bar.com have bounced before:

curl -s --user 'api:YOUR_API_KEY' -G \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces/foo@bar.com
import com.mailgun.api.v3.suppression.MailgunSuppressionBouncesApi;
import com.mailgun.model.suppression.bounces.BouncesItem;

// ...

public BouncesItem getBounce() {
    MailgunSuppressionBouncesApi suppressionBouncesApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionBouncesApi.class);

    return suppressionBouncesApi.getBounce(YOUR_DOMAIN_NAME, "foo@bar.com");
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;

# Instantiate the client.
$mgClient  = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain    = 'YOUR_DOMAIN_NAME';
$recipient = 'bob@example.com';

# Issue the call to the client.
$result = $mgClient->suppressions()->bounces()->show($domain, $recipient);
def get_bounce():
    return requests.get(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces/foo@bar.com",
        auth=("api", "YOUR_API_KEY"))
def get_bounce
  RestClient.get("https://api:YOUR_API_KEY"\
                 "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces"\
                 "/foo@bar.com"){|response, request, result| response }
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class GetBounceChunk
{

    public static void Main (string[] args)
    {
        Console.WriteLine (GetBounce ().Content.ToString ());
    }

    public static IRestResponse GetBounce ()
    {
        RestClient client = new RestClient ();
        client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
        client.Authenticator =
            new HttpBasicAuthenticator ("api",
                                        "YOUR_API_KEY");
        RestRequest request = new RestRequest ();
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.Resource = "{domain}/bounces/foo@bar.com";
        return client.Execute (request);
    }

}
import (
    "context"
    "github.com/mailgun/mailgun-go/v3"
    "time"
)

func GetBounce(domain, apiKey string) (mailgun.Bounce, error) {
    mg := mailgun.NewMailgun(domain, apiKey)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
    defer cancel()

    return mg.GetBounce(ctx, "foo@bar.com")
}
const DOMAIN = 'YOUR_DOMAIN_NAME';

const formData = require('form-data');
const Mailgun = require('mailgun.js');

const mailgun = new Mailgun(formData);

const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
  try {
    const bouncesForAddress = await client.suppressions.get(DOMAIN, 'bounces', 'foo@bar.com');
    console.log('bouncesForAddress', bouncesForAddress);
  } catch (error) {
    console.error(error);
  }
})();

Sample response:

{
  "message": "Address not found in bounces table"
}

Add a bounce to the table:

curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces \
    -F address='bob@example.com'
import com.mailgun.api.v3.suppression.MailgunSuppressionBouncesApi;
import com.mailgun.model.suppression.SuppressionResponse;
import com.mailgun.model.suppression.bounces.BouncesRequest;

import java.time.ZonedDateTime;

// ...

public SuppressionResponse addBounce() {
    MailgunSuppressionBouncesApi suppressionBouncesApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionBouncesApi.class);

    BouncesRequest bouncesRequest = BouncesRequest.builder()
        .address("bob@example.com")
        .code("550")
        .error(ERROR_MESSAGE)
        .createdAt(ZonedDateTime.now())
        .build();

    return suppressionBouncesApi.addBounce(YOUR_DOMAIN_NAME, bouncesRequest);
}
# Include the Autoloader (see "Libraries" for install instructions)
require 'vendor/autoload.php';
use Mailgun\Mailgun;

# Instantiate the client.
$mgClient = Mailgun::create('PRIVATE_API_KEY', 'https://API_HOSTNAME');
$domain   = 'YOUR_DOMAIN_NAME';
$recipient = 'bob@example.com';

# Issue the call to the client.
$result = $mgClient->suppressions()->bounces()->create($domain, $recipient);
def add_bounce():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces",
        auth=("api", "YOUR_API_KEY"),
        data={'address':'bob@example.com'})
def add_bounce
  RestClient.post("https://api:YOUR_API_KEY"\
                  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces",
                  :address => 'bob@example.com')
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class AddBounceChunk
{

    public static void Main (string[] args)
    {
        Console.WriteLine (AddBounce ().Content.ToString ());
    }

    public static IRestResponse AddBounce ()
    {
        RestClient client = new RestClient ();
        client.BaseUrl = new Uri ("https://api.mailgun.net/v3");
        client.Authenticator =
            new HttpBasicAuthenticator ("api",
                                        "YOUR_API_KEY");
        RestRequest request = new RestRequest ();
        request.Resource = "{domain}/bounces";
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.AddParameter ("address", "bob@example.com");
        request.Method = Method.POST;
        return client.Execute (request);
    }

}
import (
    "context"
    "github.com/mailgun/mailgun-go/v3"
    "time"
)

func AddBounce(domain, apiKey string) error {
    mg := mailgun.NewMailgun(domain, apiKey)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
    defer cancel()

    return mg.AddBounce(ctx, "bob@example.com", "550", "Undeliverable message error")
}
const DOMAIN = 'YOUR_DOMAIN_NAME';

const formData = require('form-data');
const Mailgun = require('mailgun.js');

const mailgun = new Mailgun(formData);

const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
    try {
        const createdBounce = await client.suppressions.create(DOMAIN, 'bounces', { address: 'bob@example.com' });
        console.log('createdBounce', createdBounce);
    } catch (error) {
        console.error(error);
    }
})();

Sample response:

{
  "message": "Address has been added to the bounces table",
  "address": "bob@example.com"
}