Site icon Mailtrap

Send emails in Go using Gmail SMTP and API

In this tutorial, I’ll show you how to send emails from your Go project using Gmail.

First, I’ll break down the sending logic paired with Gmail SMTP, and then, I’ll explain how to use Gmail API.

The code snippets I provide are compatible with Go 1.13 and above.

Send email in Go using Gmail SMTP

Here’s what you need to do to start sending emails from your Go project using Gmail SMTP:

1. Obtain app-specific password

Not so long ago, to leverage Gmail SMTP, we needed to use the Less secure apps feature. However, since it’s been deprecated by Google, nowadays we need to obtain an app password.

To create one, log in to your Google account and then:

Important: Keep in mind that once you generate your app password and click ‘Done’ in the window from the screenshot above, it won’t be shown again. So, store it securely. 🔐

2. Set up your project

If you’re reading this article, I assume you have Go installed. If not, download Go 1.2 or newer from the official website.

Then, I suggest leveraging Gomail, a community-driven package that’s quite easy to use. To install it, open the terminal in your preferred code editor and run the following command:

go get gopkg.in/mail.v2

3. Configure Gomail

To start sending plain text emails with Gomail and Gmail SMTP, paste the following code into your main configuration file (e.g., main.go):

package main

import (
    "fmt"
    gomail "gopkg.in/mail.v2"
)

func main() {
    // Create a new message
    message := gomail.NewMessage()

    // Set email headers
    message.SetHeader("From", "youremail@gmail.com")
    message.SetHeader("To", "recipient1@example.com")
    message.SetHeader("Subject", "This is an email sent via Gomail and Gmail SMTP")

    // Set email body
    message.SetBody("text/plain", "This is the Test Body")

    // Set up the SMTP dialer
    dialer := gomail.NewDialer("smtp.gmail.com", 587, "yourgmail@gmail.com", "your_app_password")

    // Send the email
    if err := dialer.DialAndSend(message); err != nil {
        fmt.Println("Error:", err)
        panic(err)
    } else {
        fmt.Println("Email sent successfully!")
    }
}

Finally, to run the script, execute the following command: go run.main.go.

Send HTML email

To send HTML emails, just change the message.SetBody("text/plain") to message.SetBody("text/html").

Check it out in a full code example:

package main

import (
    "fmt"
    gomail "gopkg.in/mail.v2"
)

func main() {
    // Create a new message
    message := gomail.NewMessage()

    // Set email headers
    message.SetHeader("From", "youremail@gmail.com")
    message.SetHeader("To", "recipient1@example.com")
    message.SetHeader("Subject", "HTML email sent via Gmail SMTP")

    // Set email body to HTML format
    message.SetBody("text/html", `
        <html>
            <body>
                <h1>This is a Test Email</h1>
                <p><b>Hello!</b> This is a test email with HTML formatting.</p>
                <p>Thanks,<br>Mailtrap</p>
            </body>
        </html>
    `)

    // Set up the SMTP dialer
    dialer := gomail.NewDialer("smtp.gmail.com", 587, "yourgmail@gmail.com", "your_app_password")

    // Send the email
   if err := dialer.DialAndSend(message); err != nil {
        fmt.Println("Error:", err)
        panic(err)
    } else {
        fmt.Println("HTML Email sent successfully!")
    }
}

Pro tip: In case some of your recipients’ clients don’t support HTML, I recommend attaching a plain text version of your HTML message. You can do this with the AddAlternative function, like so:

m.SetBody("text/html", "<p>Hello!</p>")

m.AddAlternative("text/plain", "Hello!")

Send email to multiple recipients

For multiple recipients, simply add the additional email addresses you want to send to in the “To” field under the SetHeader method.

For example:

package main

import (
    "fmt"
    gomail "gopkg.in/mail.v2"
)

func main() {
    // Create a new message
    message := gomail.NewMessage()

    // Set email headers with multiple recipients
    message.SetHeader("From", "youremail@gmail.com")
    message.SetHeader("To", "abc@example.com", "xyz@example.com", "123@email.com")  // Multiple recipients
    message.SetHeader("Subject", "Test Email to Multiple Recipients")

    // Set email body
    message.SetBody("text/html", `
        <html>
            <body>
                <h1>This is a Test Email</h1>
                <p><b>Hello!</b> This is a test email sent to multiple recipients.</p>
                <p>Thanks,<br>Mailtrap</p>
            </body>
        </html>
    `)

    // Set up the SMTP dialer
    dialer := gomail.NewDialer("smtp.gmail.com", 587, "yourgmail@gmail.com", "your_app_password")

    // Send the email
    if err := dialer.DialAndSend(message); err != nil {
        fmt.Println("Error:", err)
        panic(err)
    } else {
        fmt.Println("Email sent successfully to multiple recipients!")
    }
}

Notes:

message.SetHeader("Cc", "ccperson@example.com")
message.SetHeader("Bcc", "bccperson@example.com")

Send email with attachments

For attachments, simply move the desired image/document to your project folder and specify its name under the Attach method. 

E.g., if you’d want to send an invoice, here’s what your code would look like:

package main

import (
    "fmt"
    gomail "gopkg.in/mail.v2"
)

func main() {
    // Create a new message
    message := gomail.NewMessage()

    // Set email headers
    message.SetHeader("From", "youremail@gmail.com")
    message.SetHeader("To", "abc@example.com")
    message.SetHeader("Subject", "Test Email with Attachment")

    // Set email body
    message.SetBody("text/html", `
        <html>
            <body>
                <h1>This is a Test Email</h1>
                <p><b>Hello!</b> Please find the attachment below.</p>
                <p>Thanks,<br>Mailtrap</p>
            </body>
        </html>
    `)

    // Add attachments
    message.Attach("/invoice#1.pdf")

    // Set up the SMTP dialer
    dialer := gomail.NewDialer("smtp.gmail.com", 587, "yourgmail@gmail.com", "your_app_password")

    // Send the email
    if err := dialer.DialAndSend(message); err != nil {
        fmt.Println("Error:", err)
        panic(err)
    } else {
        fmt.Println("Email sent successfully with attachments!")
    }
}

Send email in Go using Gmail API

To send emails with Gmail API and automate your sending process, follow these steps:

1. Create a project

First things first, visit the Google Developers Console and either click ‘Select a project’ ‘Create a project’, or ‘New project’ to, you’ve guessed it, create a new project. 🙂

Note: Besides giving you plenty of buttons to choose from when creating a new project, Google will also ask you to agree with the Terms of Service and pick your Country of residence if you’re visiting the page for the first time.

2. Enable Gmail API

Now, it’s time to navigate the popup menu on your left side and go to the Library page.

Once in the library, type in ‘Gmail API’ in the search bar, click on it, and press Enable.

Keep in mind that if you have multiple Golang projects, you’ll have to enable the Gmail API the same way separately for each one.

3. Create credentials and set up OAuth 2.0 

Next, we need to set up our OAuth 2.0 screen, you know, the one you see whenever you try to log in to some service with your Google account:

To enable the OAuth 2.0 screen for your Golang app, you first need to install the necessary dependencies by running the following command:

go get golang.org/x/oauth2 google.golang.org/api/gmail/v1

Then, in Google Console, select your project and navigate to API & ServicesCredentials.

Under Credentials, click on Configure, choose the user type based on your preferences, enter basic information about your app, and follow the rest of the prompts.

Then, back at the Credentials tab, click on + CREATE CREDENTIALS, choose the type of your application based on your intended use case, and for Authorized redirect URIs, add http://localhost or any other redirect URI (optional, but I do recommend it).

Lastly, download the json file from the Credentials tab and paste the OAuth credentials from it into your client_secret.json file, or, even better, a .env file for improved security. 

Here’s what the code should look like once pasted:

{
  "web": {
    "client_id": "656468997535-ifc46eev33ioj1itv8nbkcno18aglgem.apps.googleusercontent.com",
    "project_id": "formidable-rune-415719",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "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",
    "redirect_uris": [
      "http://localhost:8000/callback"
    ]
  }
}

Note: your client_secret.json and .env files should be in the same directory as your main.go file.

4. Configure your main Golang file

Lastly, here’s a code snippet you can copy into your main.go to start sending plain text emails:

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
    "google.golang.org/api/gmail/v1"
    "os"
    "log"
)

func main() {
    // Read credentials
    secret, err := os.ReadFile("client_secret.json")
    if err != nil {
        log.Fatalf("Unable to read client secret file: %v", err)
    }

    // Set up OAuth2 config
    conf, err := google.ConfigFromJSON(secret, gmail.GmailSendScope)
    if err != nil {
        log.Fatalf("Unable to parse client secret file to config: %v", err)
    }

    // Generate auth URL
    url := conf.AuthCodeURL("CSRF", oauth2.AccessTypeOffline)
    fmt.Printf("Visit this URL to get an authorization code: \n%v\n", url)

    // Input auth code
    var code string
    fmt.Print("Enter the authorization code: ")
    _, err = fmt.Scan(&code)
    if err != nil {
        log.Fatalf("Unable to read authorization code: %v", err)
    }

    // Exchange auth code for access token
    tok, err := conf.Exchange(context.Background(), code)
    if err != nil {
        log.Fatalf("Unable to retrieve token from web: %v", err)
    }

    // Create HTTP client using the access token
    client := conf.Client(context.Background(), tok)

    // Initialize Gmail API service
    gmailService, err := gmail.New(client)
    if err != nil {
        log.Fatalf("Unable to create Gmail service: %v", err)
    }

    // Create email message
    var message gmail.Message
    messageStr := []byte(
        "From: youremail@gmail.com\r\n" +
            "To: recipient@gmail.com\r\n" +
            "Subject: My first Gmail API message\r\n\r\n" +
            "Message body goes here!")

    // Encode message in base64 URL-safe format
    message.Raw = base64.URLEncoding.EncodeToString(messageStr)

    // Send email
    _, err = gmailService.Users.Messages.Send("me", &message).Do()
    if err != nil {
        log.Fatalf("Unable to send email: %v", err)
    } else {
        fmt.Println("Email sent successfully!")
    }
}

To run the script, execute go run main.go.

Send HTML email

If you want to send HTML email, set the content type in the messageStr section to "text/html"

For example:

messageStr := []byte(
    "From: youremail@gmail.com\r\n" +
        "To: recipient@gmail.com\r\n" +
        "Subject: My first HTML email with Gmail API\r\n" +
        "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
        "<html><body><h1>Hello!</h1><p>This is an <b>HTML</b> email.</p></body></html>")

And here’s the full code for your convenience:

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
    "google.golang.org/api/gmail/v1"
    "os"
    "log"
)

func main() {
    // Read credentials
    secret, err := os.ReadFile("client_secret.json")
    if err != nil {
        log.Fatalf("Unable to read client secret file: %v", err)
    }

    // Set up OAuth2 config
    conf, err := google.ConfigFromJSON(secret, gmail.GmailSendScope)
    if err != nil {
        log.Fatalf("Unable to parse client secret file to config: %v", err)
    }

    // Generate auth URL
    url := conf.AuthCodeURL("CSRF", oauth2.AccessTypeOffline)
    fmt.Printf("Visit this URL to get an authorization code: \n%v\n", url)

    // Input auth code
    var code string
    fmt.Print("Enter the authorization code: ")
    _, err = fmt.Scan(&code)
    if err != nil {
        log.Fatalf("Unable to read authorization code: %v", err)
    }

    // Exchange auth code for access token
    tok, err := conf.Exchange(context.Background(), code)
    if err != nil {
        log.Fatalf("Unable to retrieve token from web: %v", err)
    }

    // Create HTTP client using the access token
    client := conf.Client(context.Background(), tok)

    // Initialize Gmail API service
    gmailService, err := gmail.New(client)
    if err != nil {
        log.Fatalf("Unable to create Gmail service: %v", err)
    }

    // Create email message in HTML format
    var message gmail.Message
    messageStr := []byte(
        "From: youremail@gmail.com\r\n" +
            "To: recipient@gmail.com\r\n" +
            "Subject: My first HTML email with Gmail API\r\n" +
            "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
            "<html><body><h1>Hello!</h1><p>This is an <b>HTML</b> email.</p></body></html>")

    // Encode message in base64 URL-safe format
    message.Raw = base64.URLEncoding.EncodeToString(messageStr)

    // Send email
    _, err = gmailService.Users.Messages.Send("me", &message).Do()
    if err != nil {
        log.Fatalf("Unable to send email: %v", err)
    } else {
        fmt.Println("Email sent successfully!")
    }
}

Bonus tip: If you want to send both plain text and HTML emails at the same time, simply use boundary to separate the two in your messageStr, like so:

 messageStr := []byte(
        "From: youremail@gmail.com\r\n" +
            "To: recipient@gmail.com\r\n" +
            "Subject: My email with plain text and HTML\r\n" +
            "MIME-Version: 1.0\r\n" +
            "Content-Type: multipart/alternative; boundary=\"" + boundary + "\"\r\n\r\n" +
            "--" + boundary + "\r\n" +
            "Content-Type: text/plain; charset=UTF-8\r\n\r\n" +
            "This is the plain-text version of the email.\r\n\r\n" +
            "--" + boundary + "\r\n" +
            "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
            "<html><body><h1>Hello!</h1><p>This is an <b>HTML</b> email.</p></body></html>\r\n\r\n" +
            "--" + boundary + "--")

Send email to multiple recipients

For sending emails to multiple recipients, add the addresses you want to send to in the To field, and, of course, separate them with commas.

Check it out:

"To: recipient1@example.com, recipient2@example.com, 
recipient3@example.com\r\n" + // Multiple recipients

Additionally, you can use Cc and Bcc, like so:

"Cc: cc@example.com\r\n" +
"Bcc: bcc@example.com\r\n" +

Send email with attachments

To send emails with attachments, encode the file you want to add in base64 and specify it as part of the MIME structure.

Don’t worry, here’s a code snippet you can use:

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/google"
    "google.golang.org/api/gmail/v1"
    "os"
    "log"
)

func main() {
    // Read client secrets
    secret, err := os.ReadFile("client_secret.json")
    if err != nil {
        log.Fatalf("Unable to read client secret file: %v", err)
    }

    // Set up OAuth2 config
    conf, err := google.ConfigFromJSON(secret, gmail.GmailSendScope)
    if err != nil {
        log.Fatalf("Unable to parse client secret file to config: %v", err)
    }

    // Generate auth URL
    url := conf.AuthCodeURL("CSRF", oauth2.AccessTypeOffline)
    fmt.Printf("Visit this URL to get an authorization code: \n%v\n", url)

    // Read authorization code
    var code string
    fmt.Print("Enter the authorization code: ")
    _, err = fmt.Scan(&code)
    if err != nil {
        log.Fatalf("Unable to read authorization code: %v", err)
    }

    // Exchange the auth code for access token
    tok, err := conf.Exchange(context.Background(), code)
    if err != nil {
        log.Fatalf("Unable to retrieve token from web: %v", err)
    }

    // Create a new Gmail client
    client := conf.Client(context.Background(), tok)
    gmailService, err := gmail.New(client)
    if err != nil {
        log.Fatalf("Unable to create Gmail service: %v", err)
    }

    // Load the attachment file
    filePath := "path/to/your/file.pdf" // specify your file path here
    fileData, err := ioutil.ReadFile(filePath)
    if err != nil {
        log.Fatalf("Unable to read the file: %v", err)
    }
    encodedFile := base64.StdEncoding.EncodeToString(fileData)

    // Construct the MIME message
    boundary := "my-boundary-12345"
    messageBody := "--" + boundary + "\r\n" +
        "Content-Type: text/plain; charset=UTF-8\r\n\r\n" +
        "This is a plain-text email body.\r\n\r\n" +
        "--" + boundary + "\r\n" +
        "Content-Type: application/pdf; name=\"file.pdf\"\r\n" +
        "Content-Disposition: attachment; filename=\"file.pdf\"\r\n" +
        "Content-Transfer-Encoding: base64\r\n\r\n" +
        encodedFile + "\r\n" +
        "--" + boundary + "--"

    // Create the full message with headers
    fullMessage := "To: recipient@example.com\r\n" +
        "Subject: Email with Attachment\r\n" +
        "MIME-Version: 1.0\r\n" +
        "Content-Type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n" +
        messageBody

    // Base64 encode the message
    var message gmail.Message
    message.Raw = base64.URLEncoding.EncodeToString([]byte(fullMessage))

    // Send the email
    _, err = gmailService.Users.Messages.Send("me", &message).Do()
    if err != nil {
        log.Fatalf("Unable to send email: %v", err)
    } else {
        fmt.Println("Email sent successfully!")
    }
}

Gmail limitations

To ensure fair usage and protect its email infrastructure, Google has designed and implemented various limitations.

The most notable ones include:

For official Gmail SMTP limitations, click here — for Gmail API limitations, click here

Is there an alternative to Gmail? 

Yes, there is, and it’s called Mailtrap!

My dear colleague Veljko, an expert on all things email, and especially Gmail, has written an article after a thorough research on the best SMTP providers, and Mailtrap came out on top! 

Mailtrap Email Delivery Platform offers both an SMTP service and an email API designed for reliable email delivery in production environments. 

Besides the features in the video Mailtrap also offers:

Now, let me show you how easy it is to set up Mailtrap!

Setup guide for configuring Mailtrap

P.S. We have a detailed Knowledge Base article to help you through this 5-minute process. 👀

With your domain verified, you can start sending emails from your Go project either via SMTP or API.

SMTP

Navigate to Sending Domains Integration, choose your preferred stream, and copy the provided Mailtrap credentials to your Go configuration.

For example, if you are using Gomail, your Main.go file should look like this:

package main

import (
    "fmt"
    gomail "gopkg.in/mail.v2"
)

func main() {
    // Create a new message
    message := gomail.NewMessage()

    // Set email headers
    message.SetHeader("From", "youremail@email.com")
    message.SetHeader("To", "recipient1@email.com")
    message.SetHeader("Subject", "Hello from the Mailtrap team")

    // Set email body
    message.SetBody("text/plain", "This is the Test Body")

    // Set up the SMTP dialer
    dialer := gomail.NewDialer("live.smtp.mailtrap.io", 587, "api", "1a2b3c4d5e6f7g")

    // Send the email
    if err := dialer.DialAndSend(message); err != nil {
        fmt.Println("Error:", err)
        panic(err)
    } else {
        fmt.Println("Email sent successfully!")
    }
}

For a step-by-step guide for sending emails in Golang using Mailtrap SMTP/API, be sure to consult our dedicated article.

Email API

Navigate to Sending Domains Integration, choose your preferred stream, select API, and copy the snippet below into your main.go file:

package main

import (

"bytes"

"fmt"

"io"

"log"

"net/http"

"time"

)

func main() {

// Mailtrap account config

token := "<secret_token>"

httpHost := "https://send.api.mailtrap.io/api/send"

// Message body

message := []byte(`{"from":{"email":"john.doe@your.domain"},

"to":[{"email":"kate.doe@example.com"}],

"subject":"Why aren’t you using Mailtrap yet?",

"text":"Here’s the space for your great sales pitch"}`)

// Set up request

request, err := http.NewRequest(http.MethodPost, httpHost, bytes.NewBuffer(message))

if err != nil {

log.Fatal(err)

}

// Set required headers

request.Header.Set("Content-Type", "application/json")

request.Header.Set("Authorization", "Bearer "+token)

// Send request

client := http.Client{Timeout: 5 * time.Second}

res, err := client.Do(request)

if err != nil {

log.Fatal(err)

}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)

if err != nil {

log.Fatal(err)

}

fmt.Println(string(body))

}

Test emails before sending

Adding sending logic to your app/project is only half of the battle won. For the other half, you must test your emails to ensure you have a flawless email-sending cycle. 

Email testing is an industry-standard practice that ensures your HTML/CSS designs are rendered properly across different browsers/clients, that your emails pass spam filters, or, even worse, that your domain isn’t getting blacklisted. 🤕

For this purpose, I use Mailtrap Email Testing, which, along with Mailtrap API/SMTP, is another inseparable component of Mailtrap Email Delivery Platform. With it, I can safely test my emails without the risk of sending them to recipients.

Mailtrap Email Testing features:

Now, let me show you how Mailtrap Email Testing works!

SMTP

First, you need to create a free Mailtrap account. Then, simply navigate to Email Testing Inboxes Integration

Once there, copy the provided fake SMTP credentials into your Gomail configuration, more specifically, in your dialer method, like so:

dialer := gomail.NewDialer("sandbox.smtp.mailtrap.io", 587, "ae54a7713017172", "1a2b3c4d5e6f7g")

API

You can also integrate Mailtrap API for testing, automation, and testing automated sequences. 

Go to Email Testing Inboxes Integration, select API, then Go underneath the Code Samples.

Lastly, to send a plain-text email, copy the snippet below into your main.go file:

package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://sandbox.api.mailtrap.io/api/send/2804194"
	method := "POST"

	payload := strings.NewReader(`{\"from\":{\"email\":\"hello@example.com\",\"name\":\"Mailtrap Test\"},\"to\":[{\"email\":\"demo@mailtrap.io\"}],\"subject\":\"You are awesome!\",\"text\":\"Congrats for sending test email with Mailtrap!\",\"category\":\"Integration Test\"}`)

	client := &http.Client {
	}
	req, err := http.NewRequest(method, url, payload)

	if err != nil {
		fmt.Println(err)
		return
	}
	req.Header.Add("Authorization", "Bearer 0e0****************************as9")
	req.Header.Add("Content-Type", "application/json")

	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(body))
}

For more use cases and information, consult the official API documentation.

Wrapping up

With email testing covered, you now have everything you need for a solid email sending cycle! 

However, should you use Gmail’s email infrastructure for your Golang project? If you can look past its limitations, sure, go for Gmail’s SMTP or API.

On the other hand, if you’re a developer or a product manager who needs a reliable and efficient solution, Mailtrap is the obvious choice.

Regardless of what you choose, I wish you good luck! And be sure to check out our blog for more great tutorials. Or, visit our YouTube channel, which is just an eye-candy! 🍭

Exit mobile version