How to start tracking email events

Once you start sending and receiving messages, it’s important to track what’s happening with them. Mailgun provides a variety of methods to access data about your emails, which you can read more about in the Tracking Messages section of the User Manual. Below is a brief summary of Events, the Events API and Events Webhooks.


Mailgun keeps track of every event that happens to every message (both inbound and outbound) and stores this data for at least 30 days for paid accounts and 2 days for free accounts.

Below is the table of events that Mailgun tracks.

Event Description
accepted Mailgun accepted the request to send/forward the email and the message has been placed in queue.
rejected Mailgun rejected the request to send/forward the email.
delivered Mailgun sent the email and it was accepted by the recipient email server.
failed Mailgun could not deliver the email to the recipient email server.
opened The email recipient opened the email and enabled image viewing. Open tracking must be enabled in the Mailgun control panel, and the CNAME record must be pointing to
clicked The email recipient clicked on a link in the email. Click tracking must be enabled in the Mailgun control panel, and the CNAME record must be pointing to
unsubscribed The email recipient clicked on the unsubscribe link. Unsubscribe tracking must be enabled in the Mailgun control panel.
complained The email recipient clicked on the spam complaint button within their email client. Feedback loops enable the notification to be received by Mailgun.
stored Mailgun has stored an incoming message

You can access Events through a few interfaces:

  • Webhooks (we POST data to your URL).
  • The Events API (you GET data through the API).
  • The Logs tab of the Control Panel (GUI).

Events API

You can programmatically query and download events through the Events API:

curl -s --user 'api:YOUR_API_KEY' -G \ \
      --data-urlencode begin='Fri, 3 May 2013 09:00:00 -0000' \
      --data-urlencode ascending=yes \
      --data-urlencode limit=25 \
      --data-urlencode pretty=yes \
import com.mailgun.api.v3.MailgunEventsApi;

import java.time.ZoneId;
import java.time.ZonedDateTime;

// ...

public EventsResponse getEvents() {
    MailgunEventsApi mailgunEventsApi = MailgunClient.config(API_KEY)

    EventsQueryOptions eventsQueryOptions = EventsQueryOptions.builder()

    return mailgunEventsApi.getEvents(YOUR_DOMAIN_NAME, eventsQueryOptions);
# 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';
$queryString = array(
    'begin'        => 'Wed, 1 Jan 2020 09:00:00 -0000',
    'ascending'    => 'yes',
    'limit'        =>  25,
    'pretty'       => 'yes',
    'recipient'    => ''

# Issue the call to the client.
$result = $mgClient->events()->get($domain, $queryString);
def get_logs():
    return requests.get(
        auth=("api", "YOUR_API_KEY"),
        params={"begin"       : "Fri, 3 May 2013 09:00:00 -0000",
                "ascending"   : "yes",
                "limit"       :  25,
                "pretty"      : "yes",
                "recipient" : ""})
def get_logs
  RestClient.get "https://api:YOUR_API_KEY"\
   :params => {
    :'begin'       => 'Fri, 3 May 2013 09:00:00 -0000',
    :'ascending'   => 'yes',
    :'limit'       =>  25,
    :'pretty'      => 'yes',
    :'recipient' => ''
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class EventsDateTimeRecipientChunk

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

    public static IRestResponse EventsDateTimeRecipient ()
        RestClient client = new RestClient ();
        client.BaseUrl = new Uri ("");
        client.Authenticator =
            new HttpBasicAuthenticator ("api",
        RestRequest request = new RestRequest ();
        request.AddParameter ("domain", "YOUR_DOMAIN_NAME", ParameterType.UrlSegment);
        request.Resource = "{domain}/events";
        request.AddParameter ("begin", "Fri, 3 May 2013 09:00:00 -0000");
        request.AddParameter ("ascending", "yes");
        request.AddParameter ("limit", 25);
        request.AddParameter ("pretty", "yes");
        request.AddParameter ("recipient", "");
        return client.Execute (request);

import (

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

    // Create an iterator
    it := mg.ListEvents(&mailgun.ListEventOptions{
        Begin: time.Now().Add(-50 * time.Minute),
        Limit: 100,
        Filter: map[string]string{
            "recipient": "",

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

    // Iterate through all the pages of events
    var page []mailgun.Event
    for it.Next(ctx, &page) {
        for _, event := range page {
            fmt.Printf("%+v\n", event)

    // Did iteration end because of an error?
    if it.Err() != nil {
        return it.Err()

    return nil

import formData from 'form-data';
import Mailgun from 'mailgun.js';

const mailgun = new Mailgun(formData);

const client = mailgun.client({ username: 'api', key: 'YOUR_API_KEY' || '' });
(async () => {
  try {
    const date = new Date(2021, 10, 1, 0, 0, 0, 0); // Mon Nov 01 2021 00:00:00 GMT+0200
    const events = await, {
      begin: date.toGMTString(), // Sun, 31 Oct 2021 22:00:00 GMT
      ascending: 'yes',
      limit: 5
    console.log('events', events)
  } catch (error) {

Sample response:

  "items": [
      "tags": [],
      "timestamp": 1376325780.160809,
      "envelope": {
        "sender": "",
        "transport": ""
      "event": "accepted",
      "campaigns": [],
      "user-variables": {},
      "flags": {
        "is-authenticated": true,
        "is-test-mode": false
      "message": {
        "headers": {
          "to": "",
          "message-id": "",
          "from": "Excited User <>",
          "subject": "Hello"
        "attachments": [],
        "recipients": [
        "size": 69
      "recipient": "",
      "method": "http"
  "paging": {

Events Webhooks

Mailgun can also make an HTTP POST to your URLs when events occur with your messages. If you would like Mailgun to POST event notifications, you need to provide a callback URL in the respective tab of the Control Panel. Webhooks are at the domain level so you can provide a unique URL for each domain by using the domain drop down selector.

You can read more about the data that is posted in the Webhooks section of the User Manual. We recommend using for creating temporary URLs to test and debug your webhooks.

Other Goodies

In addition to sending, receiving and storing mail, Mailgun can also help developers with the following:

  • Automatic “Unsubscribe me” functionality.
  • Support for email campaigns and tracking their performance via tags.
  • Bounce handling.
  • Spam complaints handling.
  • Spam filtering for incoming messages.
  • Searchable email logs.

The list of what Mailgun can do for you is growing every day. Please take a look at our User Manual to learn more.