How to start sending email


Send with SMTP or API

It’s up to you, whatever you find easier is fine with us. Here’s something to consider:

Flexible with existing apps Faster (x3)
Open protocol Assembly (don't worry about MIME)
Scales better (Batch sending)

In short, SMTP is an open and established protocol with large ecosystem, while Mailgun API is better long term performance and maintenance wise.

Send via API

Run this:

curl -s --user 'api:YOUR_API_KEY' \ \
    -F from='Excited User <mailgun@YOUR_DOMAIN_NAME>' \
    -F \
    -F subject='Hello' \
    -F text='Testing some Mailgun awesomeness!'
import com.mailgun.api.v3.MailgunMessagesApi;
import com.mailgun.model.message.Message;
import com.mailgun.model.message.MessageResponse;

// ...

public MessageResponse sendSimpleMessage() {
    MailgunMessagesApi mailgunMessagesApi = MailgunClient.config(API_KEY)

    Message message = Message.builder()
        .from("Excited User <USER@YOURDOMAIN.COM>")
        .text("Testing out some Mailgun awesomeness!")

    return mailgunMessagesApi.sendMessage(YOUR_DOMAIN_NAME, message);
# 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";
$params = array(
  'from'    => 'Excited User <YOU@YOUR_DOMAIN_NAME>',
  'to'      => '',
  'subject' => 'Hello',
  'text'    => 'Testing some Mailgun awesomness!'

# Make the call to the client.
$mgClient->messages()->send($domain, $params);
def send_simple_message():
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})
def send_simple_message "https://api:YOUR_API_KEY"\
  :from => "Excited User <mailgun@YOUR_DOMAIN_NAME>",
  :to => ", YOU@YOUR_DOMAIN_NAME",
  :subject => "Hello",
  :text => "Testing some Mailgun awesomness!"
using System;
using System.IO;
using RestSharp;
using RestSharp.Authenticators;

public class SendSimpleMessageChunk

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

    public static IRestResponse SendSimpleMessage ()
        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}/messages";
        request.AddParameter ("from", "Excited User <mailgun@YOUR_DOMAIN_NAME>");
        request.AddParameter ("to", "");
        request.AddParameter ("to", "YOU@YOUR_DOMAIN_NAME");
        request.AddParameter ("subject", "Hello");
        request.AddParameter ("text", "Testing some Mailgun awesomness!");
        request.Method = Method.POST;
        return client.Execute (request);

import (

func SendSimpleMessage(domain, apiKey string) (string, error) {
    mg := mailgun.NewMailgun(domain, apiKey)
    m := mg.NewMessage(
        "Excited User <mailgun@YOUR_DOMAIN_NAME>",
        "Testing some Mailgun awesomeness!",

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

    _, id, err := mg.Send(ctx, m)
    return id, err

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

const mailgun = new Mailgun(formData);
const client = mailgun.client({username: 'api', key: API_KEY});

const messageData = {
  from: 'Excited User <>',
  to: ',',
  subject: 'Hello',
  text: 'Testing some Mailgun awesomeness!'

client.messages.create(DOMAIN, messageData)
 .then((res) => {
 .catch((err) => {

NOTE: If you’re sending from our EU infrastructure, be sure to substitute the beginning of the endpoint “” with “”

What actually happened:

  • Mailgun assembled a MIME message.
  • Added the log entries to our full text search index.
  • Delivered the email.

You can find your private API key on your dashboard.

Send via SMTP

Run this:

# Swaks is an smtp of CURL, install it first:
curl -o swaks
# Set the permissions for the script so you can run it
chmod +x swaks
# It's based on perl, so install perl
sudo apt-get -y install perl
# now send!
./swaks --auth \
        --server \
        --au postmaster@YOUR_DOMAIN_NAME \
        --ap 3kh9umujora5 \
        --to \
        --h-Subject: "Hello" \
        --body 'Testing some Mailgun awesomness!'
import java.util.Properties;
import java.util.Date;
import javax.mail.*;
import javax.mail.internet.*;
import com.sun.mail.smtp.*;

public class MGSendSimpleSMTP {

    public static void main(String args[]) throws Exception {

        Properties props = System.getProperties();
        props.put("", "");
        props.put("mail.smtps.auth", "true");

        Session session = Session.getInstance(props, null);
        Message msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress("YOU@YOUR_DOMAIN_NAME"));

        InternetAddress[] addrs = InternetAddress.parse("", false));
        msg.setRecipients(Message.RecipientType.TO, addrs)

        msg.setText("Testing some Mailgun awesomness");
        msg.setSentDate(new Date());

        SMTPTransport t =
            (SMTPTransport) session.getTransport("smtps");
        t.connect("", "postmaster@YOUR_DOMAIN_NAME", "YOUR_SMTP_PASSWORD");
        t.sendMessage(msg, msg.getAllRecipients());

        System.out.println("Response: " + t.getLastServerResponse());

// Using Awesome
require 'PHPMailerAutoload.php';

$mail = new PHPMailer;

$mail->isSMTP();                                      // Set mailer to use SMTP
$mail->Host = '';                     // Specify main and backup SMTP servers
$mail->SMTPAuth = true;                               // Enable SMTP authentication
$mail->Username = 'postmaster@YOUR_DOMAIN_NAME';   // SMTP username
$mail->Password = 'secret';                           // SMTP password
$mail->SMTPSecure = 'tls';                            // Enable encryption, only 'tls' is accepted

$mail->From = 'YOU@YOUR_DOMAIN_NAME';
$mail->FromName = 'Mailer';
$mail->addAddress('');                 // Add a recipient

$mail->WordWrap = 50;                                 // Set word wrap to 50 characters

$mail->Subject = 'Hello';
$mail->Body    = 'Testing some Mailgun awesomness';

if(!$mail->send()) {
    echo 'Message could not be sent.';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
    echo 'Message has been sent';
import smtplib

from email.mime.text import MIMEText

msg = MIMEText('Testing some Mailgun awesomness')
msg['Subject'] = "Hello"
msg['From']    = "foo@YOUR_DOMAIN_NAME"
msg['To']      = ""

s = smtplib.SMTP('', 587)

s.login('postmaster@YOUR_DOMAIN_NAME', '3kh9umujora5')
s.sendmail(msg['From'], msg['To'], msg.as_string())
# install `mail` gem first: `gem install mail`

require 'mail'

Mail.defaults do
  delivery_method :smtp, {
    :port      => 587,
    :address   => "",
    :user_name => "",
    :password  => "",

mail = Mail.deliver do
  to      ''
  from    'foo@YOUR_DOMAIN_NAME'
  subject 'Hello'

  text_part do
    body 'Testing some Mailgun awesomness'
using System;
using System.IO;
using MailKit;
using MailKit.Net.Smtp;
using MimeKit;
using RestSharp;
using RestSharp.Authenticators;

public class SmtpMessageChunk

    public static void Main (string[] args)
        SendMessageSmtp ();

    public static void SendMessageSmtp ()
        // Compose a message
        MimeMessage mail = new MimeMessage ();
        mail.From.Add (new MailboxAddress ("Excited Admin", "foo@YOUR_DOMAIN_NAME"));
        mail.To.Add (new MailboxAddress ("Excited User", ""));
        mail.Subject = "Hello";
        mail.Body = new TextPart ("plain") {
            Text = @"Testing some Mailgun awesomesauce!",

        // Send it!
        using (var client = new SmtpClient ()) {
            // XXX - Should this be a little different?
            client.ServerCertificateValidationCallback = (s, c, h, e) => true;

            client.Connect ("", 587, false);
            client.AuthenticationMechanisms.Remove ("XOAUTH2");
            client.Authenticate ("postmaster@YOUR_DOMAIN_NAME", "3kh9umujora5");

            client.Send (mail);
            client.Disconnect (true);

import (

func main() {
    e := email.NewEmail()
    e.From = "Your Name <foo@YOUR_DOMAIN_NAME>"
    e.To = []string{""}
    e.Subject = "Hello"
    e.Text = []byte("Testing some Mailgun awesomeness")
    err := e.Send("", smtp.PlainAuth("", "YOUR_USERNAME", "YOUR_PASSWORD", ""))
    if err != nil {
// Using Nodemailer
import nodemailer from 'nodemailer';

async function main() {
 // create reusable transporter object using the default SMTP transport
 let transporter = nodemailer.createTransport({
   host: "",
   port: 587,
   auth: {
     user: "postmaster@YOUR_DOMAIN_NAME",
     pass: "YOUR_SMTP_PASSWORD",

 // send mail with defined transport object
 let info = await transporter.sendMail({
   from: 'foo@YOUR_DOMAIN_NAME',
   to: "",
   subject: "Hello",
   text: "Testing some Mailgun awesomness"

 console.log("Message sent: %s", info.messageId);


You can find your SMTP credentials for each domain on your domains tab.

Verify Your Domain

Add a domain you own and verify it by setting up the DNS records we provide (known as SPF and DKIM) at your DNS provider.


Why you need to verify your domain:

  • To prove that you are an authorized sender for the domain.
  • Verified domains are not subject to a sending limit of 300 emails per day.
  • No more “sent via” message in your emails.
  • Establishing a positive email reputation for your own domain.
  • Mailgun is less suspicious of traffic that is being sent on verified domains and so using one reduces the likelihood of being disabled.

How to verify your domain

  1. Add your domain or subdomain.
  2. Open your DNS provider and add the two TXT DNS records provided.
  3. If you want Mailgun to track clicks and opens you can also add the CNAME record.
  4. MX records should also be added, unless you already have MX records for your domain pointed at another email service provider (e.g. Gmail).

Once you’ve added the records and they’ve propagated, your domain will be verified.


It can take 24-48 hours for DNS changes to propagate.

If you will be creating a lot of domains, Mailgun offers an API endpoint for adding/editing/removing domains from your account. See the Domains endpoint for more information.

Add Sending & Tracking DNS Records

  • SPF: Sending server IP validation. Used by majority of inbound mail servers. SPF Information.
  • DKIM: Like SPF, but uses cryptographic methods for validation. Supported by many inbound mail servers. DKIM Information
  • CNAME: Used for tracking opens and clicks, when enabled. Tracking Messages
Type Value Purpose
TXT “v=spf1 ~all” SPF (Required)
TXT Find this record in your Control Panel, Domains Tab DKIM (Required)
CNAME “” Tracking (Optional)


While the CNAME is listed as optional, it is required to enable Unsubscribe and Click tracking links.

Add Receiving MX Records

Mail server for handling inbound messages. MX Information

Type Value Purpose
MX Receiving (Optional)
MX Receiving (Optional)


Do not configure Receiving MX DNS records if you already have another provider handling inbound mail delivery (e.g. Gmail).

Common DNS Providers

Common providers are listed below. If yours is not listed, contact your DNS provider for assistance.

GoDaddy: MX - CNAME - TXT

NameCheap: All Records

Network Solutions: MX - CNAME - TXT

Rackspace Email & Apps: All Records

Rackspace Cloud DNS: Developer Guide

Amazon Route 53: Developer Guide

DigitalOcean: Mailgun and DigitalOcean Guide

You are all set!

Read more about How to start receiving inbound email and How to start tracking email events.