Suppressions

Mailgun keeps three lists of addresses it blocks the delivery to: bounces, unsubscribes and complaints. These lists are populated automatically as Mailgun detects undeliverable addresses you try to send to and as recipients unsubscribe from your mailings or mark your emails as a spam (for ESPs that provide FBL). You can also add/remove addresses from any of these lists using the API.

It’s important to note that these suppression lists are unique to a sending domain and are not an account level (global) suppression list. If you want to add/remove the same address(es) from multiple domains, you’ll need to do so for each domain.

You can determine if you have reached the last page of suppressions if the <next page URL> is equal to the <last page URL>.

Bounces

Bounce list stores events of delivery failures due to permanent recipient mailbox errors such as non-existent mailbox. Soft bounces (for example, mailbox is full) and other failures (for example, ESP rejects an email because it thinks it is spam) are not added to the list.

Subsequent delivery attempts to an address found in a bounce list are prevented to protect your sending reputation.

The bounce suppression API endpoint is available at:

v3/<domain>/bounces

Mailgun can notify your application every time a message bounces via a permanent_fail webhook.

View all bounces

GET /<domain>/bounces

Paginate over a list of bounces for a domain.

Note

Via this API method bounces are returned in the alphabetical order. If you wish to poll for the recently occurred bounces, please consider using the Events API.

Parameter Description
limit Maximum number of records to return (optional, default: 100, max: 1000)

Example:

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);
  }
})();

Expected response:

200
{
  "items":
    [
      {
        "address": "[email protected]",
        "code": "550",
        "error": "No such mailbox",
        "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
      },
      ...
    ],
  "paging":
    {
      "first": <first page URL>,
      "next": <next page URL>,
      "previous": <previous page URL>,
      "last": <last page URL>
    }
}

View a single bounce

GET /<domain>/bounces/<address>

Fetch a single bounce event by a given email address. Useful to check if a given email address has bounced before.

Example:

curl -s --user 'api:YOUR_API_KEY' -G \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces/[email protected]
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, "[email protected]");
}
# 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 = '[email protected]';

# 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/[email protected]",
        auth=("api", "YOUR_API_KEY"))
def get_bounce
  RestClient.get("https://api:YOUR_API_KEY"\
                 "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces"\
                 "/[email protected]"){|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/[email protected]";
        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, "[email protected]")
}
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', '[email protected]');
    console.log('bouncesForAddress', bouncesForAddress);
  } catch (error) {
    console.error(error);
  }
})();

Expected responses:

200
{
  "address": "[email protected]",
  "code": "550",
  "error": "No such mailbox",
  "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
}
404
{
  "message": "Address not found in bounces table"
}

Add a single bounce

POST /<domain>/bounces

Add a bounce record to the bounce list. Updates the existing record if the address is already there.

Parameter Description
address Valid email address
code Error code (optional, default: 550)
error Error description (optional, default: empty string)
created_at Timestamp of a bounce event in RFC2822 format (optional, default: current time)

Example:

curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces \
    -F address='[email protected]'
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("[email protected]")
        .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 = '[email protected]';

# 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':'[email protected]'})
def add_bounce
  RestClient.post("https://api:YOUR_API_KEY"\
                  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/bounces",
                  :address => '[email protected]')
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", "[email protected]");
        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, "[email protected]", "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: '[email protected]' });
        console.log('createdBounce', createdBounce);
    } catch (error) {
        console.error(error);
    }
})();

Expected response:

200
{
  "message": "Address has been added to the bounces table",
  "address": "[email protected]"
}

Add multiple bounces

POST /<domain>/bounces, Content-Type: application/json

Add multiple bounce records to the bounce list in a single API call.

Request body is expected to be a valid JSON encoded string containing up to 1000 bounce records in the following format.

[
  {
    "address": "[email protected]",
    "code": "550",
    "error": "Bounced",
    "created_at": "Thu, 13 Oct 2011 18:02:00 UTC"
  },
  {
    "address": "[email protected]",
    "code": "550",
    "error": "Bounced"
  },
  {
    "address": "[email protected]",
    "code": "550"
  },
  {
    "address": "[email protected]"
  }
]

Fields within each individual bounce record are the same as for the “add a single bounce” API method, with the same defaults and optionality rules.

Note

The current versions of our language libraries do not support adding multiple bounces yet.

Expected response:

200
{
  "message": "4 addresses have been added to the bounces table"
}

Import a list of bounces

POST /<domain>/bounces/import, Content-Type: multipart/form-data

Import a CSV file containing a list of addresses to add to the bounce list.

CSV file must be 25MB or under and must contain the following column headers: address,code,error,created_at

Column Description
address Valid email address
code Error code (optional, default: 550)
error Error description (optional, default: empty string)
created_at Timestamp of a bounce event in RFC2822 format (optional, default: current time)

Expected response:

200
{
  "message": "file uploaded successfully"
}

Delete a single bounce

DELETE /<domain>/bounces/<address>

Clears a given bounce event. The delivery to the deleted email address resumes until it bounces again.

Expected response:

200
{
  "message": "Bounced address has been removed"
}

Delete an entire bounce list

DELETE /<domain>/bounces

Clears all bounced email addresses for a domain. Delivery to the deleted email addresses will no longer be suppressed.

Expected response:

200
{
  "message": "Bounced addresses for this domain have been removed"
}

Unsubscribes

Unsubscribe list stores email addresses of recipients who unsubscribed from your mailings by clicking a Mailgun generated unsubscribe link.

Mailgun allows you to quickly add “Unsubscribe me” feature to your outgoing emails without any programming on your end. You can enable this in your Control Panel under your domain settings.

The unsubscribe suppression API endpoint is available at:

v3/<domain>/unsubscribes

Mailgun can notify your application every time a user unsubscribes via an unsubscribed webhook.

View all unsubscribes

GET /<domain>/unsubscribes

Paginate over a list of unsubscribes for a domain.

Note

Via this API method unsubscribes are returned in the alphabetical order. If you wish to poll for the recently occurred unsubscribes, please consider using the Events API.

Parameter Description
limit Number of records to return (optional, default: 100, max: 1000)

Example:

curl -s --user 'api:YOUR_API_KEY' -G \
     https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes
import com.mailgun.api.v3.suppression.MailgunSuppressionUnsubscribeApi;
import com.mailgun.model.suppression.unsubscribe.UnsubscribeItemResponse;

// ...

public UnsubscribeItemResponse getUnsubscribes() {
    MailgunSuppressionUnsubscribeApi suppressionUnsubscribeApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionUnsubscribeApi.class);

    return suppressionUnsubscribeApi.getAllUnsubscribe(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()->unsubscribes()->index($domain);
def get_unsubscribes():
    return requests.get(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes",
        auth=("api", "YOUR_API_KEY"))
def get_unsubscribes
  RestClient.get "https://api:YOUR_API_KEY"\
  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes"
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class GetUnsubscribesChunk
{

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

    public static IRestResponse GetUnsubscribes ()
    {
        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}/unsubscribes";
        return client.Execute (request);
    }

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

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

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

    var page, result []mailgun.Unsubscribe
    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 unsubscribes = await client.suppressions.list(DOMAIN, 'unsubscribes');
    console.log('unsubscribes', unsubscribes);
  } catch (error) {
    console.error(error);
  }
})();

Expected response:

200
{
  "items":
    [
      {
        "address": "[email protected]",
        "tag": "*",
        "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
      },
      ...
    ],
  "paging":
    {
      "first": <first page URL>,
      "next": <next page URL>,
      "previous": <previous page URL>,
      "last": <last page URL>
    }
}

View a single unsubscribe

GET /<domain>/unsubscribes/<address>

Fetch a single unsubscribe record. Can be used to check if a given address is present in the list of unsubscribed users.

Expected responses:

200
{
  "address": "[email protected]",
  "tag": "*",
  "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
}
404
{
  "message": "Address not found in unsubscribers table"
}

Add a single unsubscribe

POST /<domain>/unsubscribes

Add an address to the unsubscribe table.

Parameter Description
address Valid email address
tag Tag to unsubscribe from, use * to unsubscribe an address from all domain’s correspondence (optional, default: *)
created_at Timestamp of an unsubscribe event in RFC2822 format (optional, default: current time)

Example:

curl -s --user 'api:YOUR_API_KEY' \
   https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes \
   -F address='[email protected]' \
   -F tag='*'
import com.mailgun.api.v3.suppression.MailgunSuppressionUnsubscribeApi;
import com.mailgun.model.suppression.SuppressionResponse;
import com.mailgun.model.suppression.unsubscribe.UnsubscribeSingleItemRequest;

import java.time.ZonedDateTime;

// ...

public SuppressionResponse addUnsubscribeAll() {
    MailgunSuppressionUnsubscribeApi suppressionUnsubscribeApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionUnsubscribeApi.class);

    UnsubscribeSingleItemRequest unsubscribeSingleItemRequest = UnsubscribeSingleItemRequest.builder()
        .address("[email protected]")
        .tag("*")
        .createdAt(ZonedDateTime.now())
        .build();

    return suppressionUnsubscribeApi.addAddressToUnsubscribeTable(YOUR_DOMAIN_NAME, unsubscribeSingleItemRequest);
}
# 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 = '[email protected]';
$tag       = '*';

# Issue the call to the client.
$result = $mgClient->suppressions()->unsubscribes()->create($domain, $recipient, $tag);
def unsubscribe_from_all():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes",
        auth=("api", "YOUR_API_KEY"),
        data={'address':'[email protected]', 'tag': '*'})
def unsubscribe_from_all
  RestClient.post "https://api:YOUR_API_KEY"\
  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/unsubscribes",
  :address => '[email protected]',
  :tag => '*'
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class AddUnsubscribeAllChunk
{

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

    public static IRestResponse UnsubscribeFromAll ()
    {
        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}/unsubscribes";
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.AddParameter ("address", "[email protected]");
        request.AddParameter ("tag", "*");
        request.Method = Method.POST;
        return client.Execute (request);
    }

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

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

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

    return mg.CreateUnsubscribe(ctx, "[email protected]", "*")
}
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 createdUnsubscribe = await client.suppressions.create(DOMAIN, 'unsubscribes', { address: '[email protected]', tag: '*' });
        console.log('createdUnsubscribe', createdUnsubscribe);
    } catch (error) {
        console.error(error);
    }
})();

Expected response:

200
{
  "message": "Address has been added to the unsubscribes table",
  "address": "[email protected]"
}

Add multiple unsubscribes

POST /<domain>/unsubscribes, Content-Type: application/json

Add multiple unsubscribe records to the unsubscribe list in a single API call.

Request body is expected to be a valid JSON encoded string containing up to 1000 unsubscribe records in the following format.

[
  {
    "address": "[email protected]",
    "tags": ["some tag"],
    "created_at": "Thu, 13 Oct 2011 18:02:00 UTC"
  },
  {
    "address": "[email protected]",
    "tags": ["*"],
  },
  {
    "address": "[email protected]"
  }
]

Fields within each individual unsubscribe record are the same as for the “add a single unsubscribe” API method, with the same defaults and optionality rules.

Note

The current versions of our language libraries do not support adding multiple unsubscribes yet.

Expected response:

200
{
  "message": "3 addresses have been added to the unsubscribes table"
}

Import a list of unsubscribes

POST /<domain>/unsubscribes/import, Content-Type: multipart/form-data

Import a CSV file containing a list of addresses to add to the unsubscribe list.

CSV file must be 25MB or under and must contain the following column headers: address,tag,created_at

Column Description
address Valid email address
tags Tag to unsubscribe from, use * to unsubscribe an address from all domain’s correspondence (optional, default: *)
created_at Timestamp of an unsubscribe event in RFC2822 format (optional, default: current time)

Expected response:

200
{
  "message": "file uploaded successfully"
}

Delete a single unsubscribe

DELETE /<domain>/unsubscribes/<address>

Remove an address from the unsubscribes list. If tag parameter is not provided, completely removes an address from the list.

Parameter Description
tag Specific tag to remove (optional)

Expected response:

200
{
  "message": "Unsubscribe event has been removed"
}

Complaints

Complaint list stores email addresses of recipients who marked your messages as a spam (for ESPs that support FBL).

The complaint API endpoint is available at:

v3/<domain>/complaints

Mailgun can notify your application every time a recipient flags your message as spam via a complained webhook.

View all complaints

GET /<domain>/complaints

Paginate over a list of complaints for a domain.

Note

Via this API method complaints are returned in the alphabetical order. If you wish to poll for the recently occurred complaints, please consider using the Events API.

Parameter Description
limit Maximum number of records to return (optional, default: 100, max: 1000)

Example:

curl -s --user 'api:YOUR_API_KEY' -G \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/complaints
import com.mailgun.api.v3.suppression.MailgunSuppressionComplaintsApi;
import com.mailgun.model.suppression.complaints.ComplaintsItemResponse;

// ...

public ComplaintsItemResponse getComplaints() {
    MailgunSuppressionComplaintsApi suppressionComplaintsApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionComplaintsApi.class);

    return suppressionComplaintsApi.getAllComplaints(YOUR_DOMAIN_NAME, 2);
}
# 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()->complaints()->index($domain);
def get_complaints():
    return requests.get(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/complaints",
        auth=("api", "YOUR_API_KEY"))
def get_complaints
  RestClient.get "https://api:YOUR_API_KEY"\
  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/complaints"
end
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class GetComplaintsChunk
{

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

    public static IRestResponse GetComplaints ()
    {
        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}/complaints";
        return client.Execute (request);
    }

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

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

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

    var page, result []mailgun.Complaint
    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 complaints = await client.suppressions.list(DOMAIN, 'complaints');
    console.log('complaints', complaints);
  } catch (error) {
    console.error(error);
  }
})();

Expected response:

200
{
  "items":
    [
      {
        "address": "[email protected]",
        "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
      },
      ...
    ],
  "paging":
    {
      "first": <first page URL>,
      "next": <next page URL>,
      "previous": <previous page URL>,
      "last": <last page URL>
    }
}

View a single complaint

GET /<domain>/complaints/<address>

Fetch a single spam complaint by a given email address. This is useful to check if a particular user has complained.

Example:

curl -s --user 'api:YOUR_API_KEY' -G \
   https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/complaints/[email protected]
import com.mailgun.api.v3.suppression.MailgunSuppressionComplaintsApi;
import com.mailgun.model.suppression.complaints.ComplaintsItem;

// ...

public ComplaintsItem getComplaint() {
    MailgunSuppressionComplaintsApi suppressionComplaintsApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionComplaintsApi.class);

    return suppressionComplaintsApi.getSingleComplaint(YOUR_DOMAIN_NAME, "[email protected]");
}
# 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 = '[email protected]';

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

public class GetComplaintChunk
{

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

    public static IRestResponse GetComplaint ()
    {
        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}/complaints/[email protected]";
        return client.Execute (request);
    }

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

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

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

    return mg.GetComplaint(ctx, "[email protected]")
}
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 complaintsForAddress = await client.suppressions.get(DOMAIN, 'complaints', '[email protected]');
    console.log('complaintsForAddress', complaintsForAddress);
  } catch (error) {
    console.error(error);
  }
})();

Expected response:

200
{
  "address": "[email protected]",
  "created_at": "Fri, 21 Oct 2011 11:02:55 GMT"
}
404
{
  "message": "No spam complaints found for this address"
}

Add a single complaint

POST /<domain>/complaints

Add an address to the complaints list.

Parameter Description
address Valid email address
created_at Timestamp of a complaint event in RFC2822 format (optional, default: current time)

Example:

curl -s --user 'api:YOUR_API_KEY' \
   https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/complaints \
   -F address='[email protected]'
import com.mailgun.api.v3.suppression.MailgunSuppressionComplaintsApi;
import com.mailgun.model.suppression.SuppressionResponse;
import com.mailgun.model.suppression.complaints.ComplaintsSingleItemRequest;

import java.time.ZonedDateTime;

// ...

public SuppressionResponse addComplaint() {
    MailgunSuppressionComplaintsApi suppressionComplaintsApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionComplaintsApi.class);

    ComplaintsSingleItemRequest complaintsSingleItemRequest = ComplaintsSingleItemRequest.builder()
        .address( "[email protected]")
        .createdAt(ZonedDateTime.now())
        .build();

    return suppressionComplaintsApi.addAddressToComplaintsList(YOUR_DOMAIN_NAME, complaintsSingleItemRequest);
}
# 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 = '[email protected]';

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

public class AddComplaintChunk
{

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

    public static IRestResponse AddComplaint ()
    {
        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}/complaints";
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.AddParameter ("address", "[email protected]");
        request.Method = Method.POST;
        return client.Execute (request);
    }

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

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

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

    return mg.CreateComplaint(ctx, "[email protected]")
}
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 createdComplaint = await client.suppressions.create(DOMAIN, 'complaints', { address: '[email protected]' });
        console.log('createdComplaint', createdComplaint);
    } catch (error) {
        console.error(error);
    }
})();

Expected response:

200
{
  "message": "Address has been added to the complaints table",
  "address": "[email protected]"
}

Add multiple complaints

POST /<domain>/complaints, Content-Type: application/json

Add multiple complaint records to the complaint list in a single API call.

Request body is expected to be a valid JSON encoded string containing up to 1000 complaint records in the following format.

[
  {
    "address": "[email protected]",
    "created_at": "Thu, 13 Oct 2011 18:02:00 UTC"
  },
  {
    "address": "[email protected]"
  }
]

Fields within each individual complaint record are the same as for the “add a single unsubscribe” API method, with the same defaults and optionality rules.

Note

The current versions of our language libraries do not support adding multiple complaints yet.

Expected response:

200
{
  "message": "2 complaint addresses have been added to the complaints table"
}

Import a list of complaints

POST /<domain>/complaints/import, Content-Type: multipart/form-data

Import a CSV file containing a list of addresses to add to the complaint list.

CSV file must be 25MB or under and must contain the following column headers: address,created_at

Column Description
address Valid email address
created_at Timestamp of a complaint event in RFC2822 format (optional, default: current time)

Expected response:

200
{
  "message": "file uploaded successfully"
}

Delete a single complaint

DELETE /<domain>/complaints/<address>

Remove a given spam complaint.

Expected response:

200
{
  "message": "Spam complaint has been removed"
}

Whitelists

The whitelist API provides the ability to whitelist specific addresses from being added to bounce list. You can whitelist by domain name (i.e example.com) or by specific address (i.e. alice@example.com). Mailgun doesn’t add an address to bounce list if the address is whitelisted. This API is very useful if you test against your private services and don’t want to constantly clean up bounce lists.

View all whitelist records

GET /<domain>/whitelists

Paginate over a whitelists for a domain.

Parameter Description
limit Number of records to return (optional, default: 100, max: 1000)

Example:

curl -s --user 'api:YOUR_API_KEY' -G \
   https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists
import com.mailgun.api.v3.suppression.MailgunSuppressionWhitelistsApi;
import com.mailgun.model.suppression.whitelists.WhitelistsItemResponse;

// ...

public WhitelistsItemResponse getBounces() {
    MailgunSuppressionWhitelistsApi suppressionWhitelistsApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionWhitelistsApi.class);

    return suppressionWhitelistsApi.getAllWhitelists(YOUR_DOMAIN_NAME);
}
# Currently, the PHP SDK does not support Suppression Whiteslist endpoint.
# Consider using the following php curl function.
function add_domain_whitelist() {
  $ch = curl_init();

  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  curl_setopt($ch, CURLOPT_USERPWD, 'api:PRIVATE_API_KEY');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
  curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v3/domain.tld/whitelists');
  curl_setopt($ch, CURLOPT_POSTFIELDS, array(
      'address'=> '[email protected]')
  );

  $result = curl_exec($ch);
  curl_close($ch);

  return $result;
}
def get_whitelists():
    return requests.get(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists",
        auth=("api", "YOUR_API_KEY"))
def get_whitelists
  RestClient.get "https://api:YOUR_API_KEY"\
  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists"
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}/whitelists";
        return client.Execute (request);
    }

}
// Not supported yet
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 whitelists = await client.suppressions.list(DOMAIN, 'whitelists');
    console.log('whitelists', whitelists);
  } catch (error) {
    console.error(error);
  }
})();

Expected response:

200
{
  "items":
    [
      {
        "value": "[email protected]",
        "reason": "reason of white listing"
        "type": "address",
        "createdAt": "Fri, 21 Oct 2011 11:02:55 UTC"
      },
      {
        "value": "test.com",
        "reason": "reason of white listing"
        "type": "domain",
        "createdAt": "Fri, 21 Oct 2012 11:02:56 UTC"
      }
      ...
    ],
  "paging":
    {
      "first": <first page URL>,
      "next": <next page URL>,
      "previous": <previous page URL>,
      "last": <last page URL>
    }
}

View a single whitelist record

GET /<domain>/whitelists/<address or domain>

Fetch a single whitelist record. Can be used to check if a given address or domain is present in the whitelist table

Expected responses:

200
{
  "value": "[email protected]",
  "reason": "why the record was created"
  "type": "address",
  "createdAt": "Fri, 21 Oct 2011 11:02:55 GMT"
}
404
{
  "message": "Address/Domain not found in whitelists table"
}

Add a single whitelist record

POST /<domain>/whitelists

Add an address or domain to the whitelist table.

Parameter Description
address Valid email address if you would like to whitelist email address
domain Valid domain name if you would like whitelist entire domain name

Note

The single request accepts either one address or domain parameter

Example:

curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists \
    -F domain='example.com'
import com.mailgun.api.v3.suppression.MailgunSuppressionWhitelistsApi;
import com.mailgun.model.ResponseWithMessage;
import com.mailgun.model.suppression.whitelists.WhitelistsRequest;

// ...

public ResponseWithMessage addBounce() {
    MailgunSuppressionWhitelistsApi suppressionWhitelistsApi = MailgunClient.config(API_KEY)
        .createApi(MailgunSuppressionWhitelistsApi.class);

    WhitelistsRequest whitelistsRequest = WhitelistsRequest.builder()
        .address("[email protected]")
        .reason(REASON)
        .build();

    return suppressionWhitelistsApi.addSingleWhitelistRecord(YOUR_DOMAIN_NAME, whitelistsRequest);
}
# Currently, the PHP SDK does not support Suppression Whiteslist endpoint.
# Consider using the following php curl function.
function add_domain_whitelist() {
  $ch = curl_init();

  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  curl_setopt($ch, CURLOPT_USERPWD, 'api:PRIVATE_API_KEY');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_URL, 'https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists');
  curl_setopt($ch, CURLOPT_POSTFIELDS, array(
      'address'=> '[email protected]')
  );

  $result = curl_exec($ch);
  curl_close($ch);

  return $result;
}
def add_whitelist():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists",
        auth=("api", "YOUR_API_KEY"),
        data={'address':'example.com'})
def add_whitelist
  RestClient.post("https://api:YOUR_API_KEY"\
                  "@api.mailgun.net/v3/YOUR_DOMAIN_NAME/whitelists",
                  :domain => '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}/whitelists";
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.AddParameter ("domain", "example.com");
        request.Method = Method.POST;
        return client.Execute (request);
    }

}
// Not implemented
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 createdWhitelist = await client.suppressions.create(DOMAIN, 'whitelists', { domain: 'example.com' });
    console.log('createdWhitelist', createdWhitelist);
  } catch (error) {
      console.error(error);
  }
})();

Expected response:

200
{
  "message":"Address/Domain has been added to the whitelists table",
  "type":"domain",
  "value":"example.com"
}

Import a list of addresses and/or domains

POST /<domain>/whitelists/import, Content-Type: multipart/form-data

Import a CSV file containing a list of addresses and/or domains to add to the whitelist.

CSV file must be 25MB or under and must contain the following column headers: address,domain

Column Description
address Valid email address if you would like to whitelist email address
domain Valid domain name if you would like whitelist entire domain name

Expected response:

200
{
  "message": "file uploaded successfully"
}

Delete a single record from whitelist table

DELETE /<domain>/whitelists/<address or domain>

Remove a given record from whitelist table.

Expected response:

200
{
  "message":"Whitelist address/domain has been removed",
  "value":"[email protected]"
}