How to send emails with Go: API, SMTP & Golang packages
- Methods for sending emails with Go
- Send emails in Go with an API
- Getting started
- Send an HTML email
- Send a templated email
- Send an email with an attachment
- Send an email with an embedded image/inline attachment
- Send bulk email
- Send emails in Go with SMTP and net/smtp
- Getting started
- Send a plain-text email
- Send emails in Go using SMTP and Go-mail
- Getting started
- Send a plain-text or HTML email
- Send an email with an attachment
- Send an email with an embedded image
- Send an email to multiple recipients (bulk email)
- Tips for troubleshooting
- FAQs
When it comes to reliable email delivery, Go really shines with its lightweight concurrency and fast performance, making it ideal for scalable systems. Plus, it’s surprisingly easy to get up and running! You can go from “What am I doing?” to sending your first email with just a few lines of code. Well, there might be a little more to it than that, but you get the gist!
And because of its popularity, you have several options to choose from depending on the complexity and needs of your project. We’ll outline some of the main ways to send emails with Go, and then give step-by-step instructions for starting with each, including code snippets.
Methods for sending emails with Go
For this guide, we’ll look at using a third-party email API or SMTP server and/or native and community Golang packages.
Take a quick look at Go’s package library and you’ll see there are numerous options for sending emails. Some are tied to specific providers, and others allow you to use the provider of your choice. To narrow it down, we’ve included the most popular packages.
Third-party API | Go-mail package | Native net/smtp package | |
Setup complexity | Moderate, requires setup with an external provider | Moderate, requires configuration of an SMTP server or an external provider | Moderate, requires configuration of an SMTP server or an external provider |
Code simplicity | Simple, high-level API calls with structured requests | Clean and readable API designed for email sending | More verbose and lower-level; you handle most details yourself |
Features | Often includes advanced features such as templates, analytics, and inbound routing | Supports attachments, HTML, multiple recipients, and headers | Basic sending capability with optional authentication |
Dependencies | External API service and Go HTTP client or SDK | Go package dependency and SMTP server | SMTP server required (third-party or self-hosted) but no extra packages needed |
Authentication | API keys or OAuth (depends on provider) | SMTP authentication (PLAIN, LOGIN, etc.) | Manual SMTP authentication setup |
Scalability | High, provider handles throughput | Depends on your SMTP server’s configuration | Limited, concurrent connections must be managed manually |
Error handling | Structured responses, error codes and handling from provider | Built-in error handling for most send issues | Manual error checking required |
Deliverability | Managed by provider; often optimized for inbox placement | Depends on SMTP server setup and reputation | Depends on SMTP server setup and reputation |
Cost | Paid subscription or cost per email | Free/open source, but SMTP costs may apply | Free/open source, but SMTP costs may apply |
Security | TLS/SSL handled by provider | Supports STARTTLS and SSL connections | STARTTLS support available |
Best for | Apps that need reliable delivery and advanced features | General-purpose applications or self-hosted email sending | Lightweight tools, prototypes, or educational purposes |
Send emails in Go with an API
Let’s get started with sending via an API. As you can see in the table above, a third-party API offers a few advantages, such as additional features out of the box (like templates, analytics, domain management, etc.) and managed deliverability (so you won’t need to worry about your emails hitting inboxes).
This makes an API a solid choice for high-volume senders, apps or services for which reliable delivery is crucial, and agencies or multi-brand organizations that manage the emails for multiple senders or domains.
So let’s run through how you can send your first email in Go via an API— we’ll use MailerSend’s email API for this example.
Getting started
If you don’t already have a MailerSend account, you can create one for free. You’ll get a trial domain to play around with, but if you prefer, you can also add and verify your own domain.
Next, create an API key to use in your project. To do this, head to Integrations in the left-hand menu, then click Manage in the API tokens section.
Click Create new token and enter the details. Choose the domain you want to create the token for, select an expiration date, and allow the appropriate access. Then click Generate token.
Copy and save or download the token. After you close this page, you won’t be able to view the token again for security reasons.
Check out our help guide to learn more about creating and managing API tokens.
1. Install the MailerSend Go SDK with the following command:
go get github.com/mailersend/mailersend-go
2. Set your API key as an environment variable or add it to a .env file:
Bash:
export MAILERSEND_API_KEY="your_api_key"
Powershell:
$env:MAILERSEND_API_KEY="your_api_key"
Send an HTML email
Add the following code to your main.go file. This will send a basic HTML email; just remember to replace the sender and recipient details with your own.
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/mailersend/mailersend-go"
)
func main() {
// Create an instance of the mailersend client
ms := mailersend.NewMailersend(os.Getenv("MAILERSEND_API_KEY"))
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
subject := "Subject"
text := "This is the text content"
html := "<p>This is the HTML content</p>"
from := mailersend.From{
Name: "From name",
Email: "from@email.com",
}
recipients := []mailersend.Recipient{
{
Name: "Recipient name",
Email: "recipient@example.com",
},
}
message := ms.Email.NewMessage()
message.SetFrom(from)
message.SetRecipients(recipients)
message.SetSubject(subject)
message.SetHTML(html)
message.SetText(text)
// Send the email with error handling
res, err := ms.Email.Send(ctx, message)
if err != nil {
fmt.Printf("❌ Failed to send email: %v\n", err)
return
}
// Success message with message ID
fmt.Printf("✅ Email sent successfully! Message ID: %s\n", res.Header.Get("X-Message-Id"))
}
And run the program to send the email:
go run main.go
Send a personalized, templated email
One of the main benefits of using an API is that you can send templated emails, which can be reused, personalized, and designed really easily using one of MailerSend’s template builders.
To send a templated email, we’ll tweak the code a little to include the template ID, plus any of the values needed to populate personalization variables. This example uses the variable {{name}} in the template.
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/mailersend/mailersend-go"
)
func main() {
// Create an instance of the mailersend client
ms := mailersend.NewMailersend(os.Getenv("MAILERSEND_API_KEY"))
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
subject := "Subject"
from := mailersend.From{
Name: "From name",
Email: "from@email.com",
}
recipients := []mailersend.Recipient{
{
Name: "Recipient name",
Email: "recipient@example.com",
},
}
personalization := []mailersend.Personalization{
{
Email: "recipient@example.com",
Data: map[string]interface{}{
"name": "Recipient name",
},
},
}
message := ms.Email.NewMessage()
message.SetFrom(from)
message.SetRecipients(recipients)
message.SetSubject(subject)
message.SetTemplateID("template_ID")
message.SetPersonalization(personalization)
// Send the email with error handling
res, err := ms.Email.Send(ctx, message)
if err != nil {
fmt.Printf("❌ Failed to send email: %v\n", err)
return
}
// Success message with message ID
fmt.Printf("✅ Email sent successfully! Message ID: %s\n", res.Header.Get("X-Message-Id"))
}
Send an email with an attachment
To send an email with an attachment, we’ll add a snippet of code to our HTML email to open and encode the file, create the attachment, and add it to the message. Here’s the snippet:
// Open the file
f, err := os.Open("./file.jpg")
if err != nil {
panic(fmt.Errorf("failed to open file: %w", err))
}
defer f.Close()
// Read all bytes
contentBytes, err := io.ReadAll(f)
if err != nil {
panic(fmt.Errorf("failed to read file: %w", err))
}
// Base64 encode
encoded := base64.StdEncoding.EncodeToString(contentBytes)
// Create the attachment
attachment := mailersend.Attachment{
Filename: "file.jpg",
Content: encoded,
Disposition: "attachment",
}
// Add to the message
message.AddAttachment(attachment)
And here it is added to our HTML email:
package main
import (
"context"
"encoding/base64"
"fmt"
"io"
"os"
"time"
"github.com/mailersend/mailersend-go"
)
func main() {
// Create an instance of the mailersend client
ms := mailersend.NewMailersend(os.Getenv("MAILERSEND_API_KEY"))
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
subject := "Test email with attachment"
text := "This is the text content"
html := "<p>This is the HTML content</p>"
from := mailersend.From{
Name: "From name",
Email: "from@email.com",
}
recipients := []mailersend.Recipient{
{
Name: "Recipient name",
Email: "recipient@example.com",
},
}
tags := []string{"foo", "bar"}
message := ms.Email.NewMessage()
message.SetFrom(from)
message.SetRecipients(recipients)
message.SetSubject(subject)
message.SetHTML(html)
message.SetText(text)
message.SetTags(tags)
// Open the file
f, err := os.Open("./file.jpg")
if err != nil {
panic(fmt.Errorf("failed to open file: %w", err))
}
defer f.Close()
// Read all bytes
contentBytes, err := io.ReadAll(f)
if err != nil {
panic(fmt.Errorf("failed to read file: %w", err))
}
// Base64 encode
encoded := base64.StdEncoding.EncodeToString(contentBytes)
// Create the attachment
attachment := mailersend.Attachment{
Filename: "file.jpg",
Content: encoded,
Disposition: "attachment",
}
// Add to the message
message.AddAttachment(attachment)
// Send the email with error handling
res, err := ms.Email.Send(ctx, message)
if err != nil {
fmt.Printf("❌ Failed to send email: %v\n", err)
return
}
// Success message with message ID
fmt.Printf("✅ Email sent successfully! Message ID: %s\n", res.Header.Get("X-Message-Id"))
}
Send an email with an embedded image/inline attachment
If you want to embed an image instead of simply attaching it, we can create the attachment, set the Disposition to inline, and set an ID that we can use in our HTML to embed it.
// Inline image attachment
contentBytes, err := os.ReadFile("./image.jpg") // read file in one line
if err != nil {
panic(err)
}
encoded := base64.StdEncoding.EncodeToString(contentBytes)
message.AddAttachment(mailersend.Attachment{
Filename: "image.jpg",
Content: encoded,
Disposition: mailersend.DispositionInline,
ID: "image123", // matches cid in HTML
})
Here’s the working code for the embedded image in our HTML email:
package main
import (
"context"
"encoding/base64"
"fmt"
"os"
"time"
"github.com/mailersend/mailersend-go"
)
func main() {
// Create an instance of the mailersend client
ms := mailersend.NewMailersend(os.Getenv("MAILERSEND_API_KEY"))
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
subject := "Test email with attachment"
text := "This is the text content"
html := `<p>This is the HTML content</p>
<p>Here is an inline image:</p>
<img src="cid:image123" alt="My Image"/>`
from := mailersend.From{
Name: "From name",
Email: "from@email.com",
}
recipients := []mailersend.Recipient{
{
Name: "Recipient name",
Email: "recipient@example.com",
},
}
tags := []string{"foo", "bar"}
message := ms.Email.NewMessage()
message.SetFrom(from)
message.SetRecipients(recipients)
message.SetSubject(subject)
message.SetHTML(html)
message.SetText(text)
message.SetTags(tags)
// Inline image attachment
contentBytes, err := os.ReadFile("./image.jpg") // read file in one line
if err != nil {
panic(err)
}
encoded := base64.StdEncoding.EncodeToString(contentBytes)
message.AddAttachment(mailersend.Attachment{
Filename: "image.jpg",
Content: encoded,
Disposition: mailersend.DispositionInline,
ID: "image123", // matches cid in HTML
})
// Send the email with error handling
res, err := ms.Email.Send(ctx, message)
if err != nil {
fmt.Printf("❌ Failed to send email: %v\n", err)
return
}
// Success message with message ID
fmt.Printf("✅ Email sent successfully! Message ID: %s\n", res.Header.Get("X-Message-Id"))
}
Send bulk email
The MailerSend API has a bulk email endpoint, which makes it possible to bypass rate limiting and optimize sendings to preserve your API quota. This lets you send multiple personalized messages with a single API call. Since we’re sending with the bulk endpoint, we don’t need to use goroutines here, as it won’t impact our API call.
In the example below, we loop through the recipients and assign each one a message so that each person gets an individual email, instead of all recipients getting the same email.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/mailersend/mailersend-go"
)
func main() {
apiKey := os.Getenv("MAILERSEND_API_KEY")
if apiKey == "" {
log.Fatal("MAILERSEND_API_KEY is not set")
}
// Create client
ms := mailersend.NewMailersend(apiKey)
// Safeguard timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
from := mailersend.From{
Name: "From name",
Email: "from@email.com",
}
// Example: multiple recipients, each receives an individual email
recipients := []mailersend.Recipient{
{Name: "Amy", Email: "amy@example.com"},
{Name: "Bob", Email: "bob@example.com"},
{Name: "Lisa", Email: "lisa@example.com"},
}
var messages []*mailersend.Message
for i, r := range recipients {
msg := &mailersend.Message{
From: from,
Recipients: []mailersend.Recipient{
// each email has *only one* recipient -> no one sees each other
r,
},
Subject: fmt.Sprintf("Hello %s! Here is your subject #%d", r.Name, i+1),
Text: fmt.Sprintf("Hi %s,\nThis is your text version email #%d.", r.Name, i+1),
HTML: fmt.Sprintf("<p>Hi %s,</p><p>This is your HTML email <strong>#%d</strong>.</p>", r.Name, i+1),
}
// Basic validation
if r.Email == "" {
log.Printf("Skipping empty email at index %d", i)
continue
}
messages = append(messages, msg)
}
if len(messages) == 0 {
log.Fatal("No valid messages to send")
}
// Send bulk
resp, body, err := ms.BulkEmail.Send(ctx, messages)
if err != nil {
log.Printf("❌ Bulk send failed: %v", err)
log.Printf("Response: %v", resp)
log.Printf("Body: %s", body)
return
}
log.Println("✅ Bulk email successfully queued!")
log.Printf("Response: %v", resp)
log.Printf("Body: %s", body)
}
Send emails in Go with SMTP and net/smtp
Net/smtp is Go’s standard package for sending emails. It offers basic sending and lacks features such as attachments, HTML emails, and the ability to scale easily, making it more suited for basic, low-volume sending of text-based emails.
Getting started
You’ll need to use an SMTP server to send emails using this method, so we’ll use MailerSend again, this time using SMTP credentials.
If you don’t already have a MailerSend account, you can create one for free. You’ll get a trial domain to play around with, but if you prefer, you can add and verify your own domain.
Next, you’ll need to generate an SMTP user and credentials. To do this, go to Email, Domains and click Manage for the domain you want to use. Scroll to the SMTP section, and click Generate new user.
Enter a name for the SMTP user and click Save user. You can now use this username and password to authenticate when you send an email.
Check out our help guide to learn more about using MailerSend’s SMTP relay.
Send a plain-text email
Net/smtp is native to Go, so you won’t need to install any packages. In our example, however, we’re using environment variables set in a .env file, so we’ve installed the godotenv package. Then you can use the following code in your main.go file to send your email. Note, we’ve also set our SMTP host and port in the .env file.
package main
import (
"fmt"
"log"
"net/smtp"
"os"
"github.com/joho/godotenv"
)
func main() {
// Load .env file
err := godotenv.Load()
if err != nil {
log.Println("Warning: .env file not found, relying on system env variables")
}
// Load environment variables
smtpUsername := os.Getenv("SMTP_USERNAME")
smtpPassword := os.Getenv("SMTP_PASSWORD")
smtpHost := os.Getenv("SMTP_HOST")
smtpPort := os.Getenv("SMTP_PORT")
if smtpUsername == "" || smtpPassword == "" {
log.Fatal("SMTP_USERNAME or SMTP_PASSWORD not set in environment variables")
}
// MailerSend SMTP authentication
auth := smtp.PlainAuth("", smtpUsername, smtpPassword, smtpHost)
from := "from@email.com"
to := []string{"recipient@example.com"}
// Email message (RFC 822 format)
msg := []byte("To: recipient@example.com\r\n" +
"Subject: Test email via MailerSend SMTP Relay\r\n" +
"\r\n" +
"Hello! This is a test email sent using Go and net/smtp.\r\n")
// SMTP server address
addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
// Send email
err = smtp.SendMail(addr, auth, from, to, msg)
if err != nil {
log.Fatal("Error sending email: ", err)
}
fmt.Println("Email sent successfully!")
}
You can also use basic HTML for styling emails sent with net/smtp. You just need to add the correct MIME headers and HTML to the msg variable:
// HTML email with proper MIME headers
msg := []byte(
"To: recipient@example.com\r\n" +
"Subject: HTML Email via MailerSend SMTP Relay\r\n" +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/html; charset=\"UTF-8\"\r\n" +
"\r\n" +
"<h1 style='color:#4A90E2;'>Hello!</h1>" +
"<p>This is a <strong>HTML email</strong> sent using <code>net/smtp</code> 🎉</p>" +
"<p>You can include <em>styles, colors, links, etc.</em></p>" +
"<a href='https://mailersend.com'>Visit MailerSend</a>",
)
Send emails in Go using SMTP and Go-mail
Go-mail is a community package built upon the standard Go library and extended packages, and provides access to additional features like attachments, headers, and more. There’s a lot more functionality available than with net/smtp, plus it’s just as easy to use.
Since go-mail is a more flexible solution than net/smtp, it's a solid solution if you run your own mail server or third-party services are not allowed, want a simpler SMTP-based solution instead of API, need a solution for internal emails, or you need simple email delivery for lightweight services or CLIs.
Getting started
Here we’re using MailerSend again as our SMTP server. If you don’t already have a MailerSend account, you can create one for free. You’ll get a trial domain to play around with, but if you prefer, you can add and verify your own domain.
To generate an SMTP user and credentials, go to Email, Domains and click Manage for the domain you want to use. Scroll to the SMTP section, and click Generate new user.
Enter a name for the SMTP user and click Save user. You can now use this username and password to authenticate when you send an email.
Check out our help guide to learn more about using MailerSend’s SMTP relay.
In your Go project, install the go-mail package:
go get github.com/wneessen/go-mail
Send a plain-text or HTML email
To send a plain-text email, use the following code. We’re also using the godotenv package for our environment variables.
package main
import (
"log"
"os"
"github.com/wneessen/go-mail"
"github.com/joho/godotenv"
)
func main() {
// Load credentials from environment variables
err := godotenv.Load()
if err != nil {
log.Println("Warning: .env file not found, relying on system env variables")
}
smtpUser := os.Getenv("SMTP_USERNAME")
smtpPass := os.Getenv("SMTP_PASSWORD")
if smtpUser == "" || smtpPass == "" {
log.Fatal("SMTP_USERNAME or SMTP_PASSWORD is not set in environment")
}
msg := mail.NewMsg()
// Set From and To
if err := msg.From("from@email.com"); err != nil {
log.Fatalf("Invalid From address: %v", err)
}
if err := msg.To("recipient@example.com"); err != nil {
log.Fatalf("Invalid To address: %v", err)
}
msg.Subject("Hello from go-mail + MailerSend")
msg.SetBodyString(mail.TypeTextPlain, "Hey! This is a test email sent using go-mail and MailerSend.")
// Create SMTP client with env credentials
client, err := mail.NewClient(
"smtp.mailersend.net",
mail.WithPort(587),
mail.WithTLSPolicy(mail.TLSMandatory),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
mail.WithUsername(smtpUser),
mail.WithPassword(smtpPass),
)
if err != nil {
log.Fatalf("Failed to create mail client: %v", err)
}
defer client.Close()
// Send
if err := client.DialAndSend(msg); err != nil {
log.Fatalf("Failed to send message: %v", err)
}
log.Println("Email sent successfully!")
}
To use HTML in your email, you can just add another body to the same message and specify the MIME type as HTML.
msg.Subject("Hello from go-mail + MailerSend")
msg.SetBodyString(mail.TypeTextPlain, "Hey! This is a test email sent using go-mail and MailerSend.")
msg.SetBodyString(mail.TypeTextHTML, "<h1>Hey!</h1><p>This is a test email sent using <strong>go-mail and MailerSend</strong>.</p>")
Send an email with an attachment
Attaching a file to an email is easy—just add AttachFile along with your file path to the message. In this example, we’re attaching a file from the disk, but you can also attach via io.Reader.
package main
import (
"log"
"os"
"github.com/wneessen/go-mail"
"github.com/joho/godotenv"
)
func main() {
// Load credentials from environment variables
if err := godotenv.Load(); err != nil {
log.Println("Warning: .env file not found, relying on system env variables")
}
smtpUser := os.Getenv("SMTP_USERNAME")
smtpPass := os.Getenv("SMTP_PASSWORD")
if smtpUser == "" || smtpPass == "" {
log.Fatal("SMTP_USERNAME or SMTP_PASSWORD is not set in environment")
}
msg := mail.NewMsg()
// Set From and To
if err := msg.From("from@email.com"); err != nil {
log.Fatalf("Invalid From address: %v", err)
}
if err := msg.To("recipient@example.com"); err != nil {
log.Fatalf("Invalid To address: %v", err)
}
// Set subject and body
msg.Subject("Hello from go-mail + MailerSend")
msg.SetBodyString(mail.TypeTextPlain, "Hey! This is a test email with an attachment sent using go-mail and MailerSend.")
msg.SetBodyString(mail.TypeTextHTML, "<h1>Hey!</h1><p>This is a test email with an attachment sent using <strong>go-mail and MailerSend</strong>.</p>")
// ----------------------------
// ADD ATTACHMENT
// ----------------------------
// Example: attach "document.pdf" from current directory
msg.AttachFile("image.jpg")
// Create SMTP client with env credentials
client, err := mail.NewClient(
"smtp.mailersend.net",
mail.WithPort(587),
mail.WithTLSPolicy(mail.TLSMandatory),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
mail.WithUsername(smtpUser),
mail.WithPassword(smtpPass),
)
if err != nil {
log.Fatalf("Failed to create mail client: %v", err)
}
defer client.Close()
// Send
if err := client.DialAndSend(msg); err != nil {
log.Fatalf("Failed to send message: %v", err)
}
log.Println("Email sent successfully with attachment!")
}
Send an email with an embedded image
Rather than using AttachFile to embed a file, go-mail has a separate EmbedFile function. You can simply add the following snippet to your message, and then reference the Content ID in your HTML.
// Embed image and set email body to reference the embedded image
msg.EmbedFile(
"image.jpg",
mail.WithFileContentID("image123"), // Set the Content‑ID for CID reference
mail.WithFileName("image.jpg"), // Optional: define the filename for the embedded file
)
HTML:
msg.SetBodyString(mail.TypeTextHTML, `<h1>Hey!</h1><p>This is a test email with an embedded image sent using <strong>go-mail and MailerSend</strong>.</p><img src="cid:image123" alt="Embedded Image">`)
Send an email to multiple recipients (bulk email)
To send an email to multiple recipients, so that each one gets their own message, we’ll loop over our list of recipients. This involves creating a new message for each recipient, setting the to address for each one, and calling DialAndSend for each one.
We can speed up the process by sending the emails concurrently, using goroutines and a worker pool to prevent overloading the SMTP server by setting a maximum of 5 emails to be processed at a time. We’ve also configured it so that each worker creates its own SMTP client, since SMTP clients cannot be shared.
package main
import (
"log"
"os"
"sync"
"github.com/wneessen/go-mail"
"github.com/joho/godotenv"
)
func main() {
// Load credentials from environment variables
if err := godotenv.Load(); err != nil {
log.Println("Warning: .env file not found, relying on system env variables")
}
smtpUser := os.Getenv("SMTP_USERNAME")
smtpPass := os.Getenv("SMTP_PASSWORD")
if smtpUser == "" || smtpPass == "" {
log.Fatal("SMTP_USERNAME or SMTP_PASSWORD is not set in environment")
}
// List of recipients
recipients := []string{
"recipient1@example.com",
"recipient2@example.com",
"recipient3@example.com",
}
// -------------------------------------------------------------------
// WORKER POOL SETTINGS
// -------------------------------------------------------------------
workerCount := 5 // how many emails to process in parallel
recipientCh := make(chan string)
var wg sync.WaitGroup
// Start worker pool
for i := 0; i < workerCount; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for r := range recipientCh {
// Each worker creates its own SMTP client
client, err := mail.NewClient(
"smtp.mailersend.net",
mail.WithPort(587),
mail.WithTLSPolicy(mail.TLSMandatory),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
mail.WithUsername(smtpUser),
mail.WithPassword(smtpPass),
)
if err != nil {
log.Printf("Failed to create mail client for %s: %v", r, err)
continue
}
msg := mail.NewMsg()
if err := msg.From("from@email.com"); err != nil {
log.Printf("Invalid From address for %s: %v", r, err)
continue
}
if err := msg.To(r); err != nil {
log.Printf("Invalid recipient %s: %v", r, err)
continue
}
msg.Subject("Hello from go-mail + MailerSend")
msg.SetBodyString(mail.TypeTextPlain,
"Hey! This is a test email sent using go-mail and MailerSend.")
msg.SetBodyString(mail.TypeTextHTML,
"<h1>Hey!</h1><p>This is a test email sent using <strong>go-mail and MailerSend</strong>.</p>")
// Send
if err := client.DialAndSend(msg); err != nil {
log.Printf("Failed to send to %s: %v", r, err)
} else {
log.Printf("Email sent to %s", r)
}
client.Close()
}
}()
}
// Send jobs to the workers
for _, r := range recipients {
recipientCh <- r
}
close(recipientCh)
wg.Wait()
log.Println("All emails processed.")
}
Tips for troubleshooting
At the time of writing, the code examples in this guide have been tested and work as intended. But if your emails aren’t sending, here’s a checklist to help you get to the bottom of what could be causing them to fail.
1. Double-check your authentication
This might seem like a no-brainer, but it’s easy to add the incorrect SMTP credentials or API key, or create an API key with the wrong permissions. Heck, I forgot to set my environment variables at least once while I was testing for this article.
So the first thing you want to do is double-check that:
a) Your SMTP credentials or API key is correct
b) They have the correct permissions/are assigned to the correct domain
c) Your environment variables are loading correctly (never hard-code them)
2. Make sure you're using the correct SMTP server and port
For MailerSend, you should be using smtp.mailersend.net and port 587. You can also use port 2525 if necessary.
3. Check that you’re using the correct endpoints and methods
If you’re using an API package, check the documentation to make sure you have the right endpoint and method. For MailerSend, you can check our API Hub, GitHub, and API reference.
4. Confirm your domain verification records
Incorrectly configured SPF and DKIM records can lead to deliverability issues. Check that they are configured correctly by using an SPF and DKIM record check tool like MXToolBox. In MailerSend, you’ll be able to see if your domain is verified on the domains page.
5. Know your rate limits
If you’re sending high volumes of emails and your setup isn’t optimized for it, you could quickly hit your API or SMTP service’s rate limit. Check out the rate limiting imposed by your service, and use bulk emailing to send multiple emails in a single request. With the MailerSend API, you can send up to 500 emails in a single request, each with up to 50 recipients.
Learn about MailerSend's daily request quota.
6. Use AI to help with troubleshooting
If you’re using MailerSend, you can connect our MCP server to your AI coding environment or tool (VSCode, Claude, Cursor, ChatGPT or Gemini CLI). This will connect your account so that you can perform actions, make API calls, and access data with natural language prompts.
When it comes to troubleshooting, you can use the MCP to analyze activity and check your implementation for potential issues. For example, you could:
Ensure your domain has the proper security settings
Verify your SPF/DKIM records
Get a list of all emails that have failed for a specific time period
Analyze bounce patterns
To get started, check out MCP help guide, MCP developer guide, and MCP use cases.
FAQs
1. Which Go package should I use for sending email?
There is no one-size-fits-all approach for sending emails with Go. It depends on your sending volume, purpose for sending, features you’ll need, current setup, and limitations.
For very basic, low-volume text-only emails, testing or internal needs, net/smtp could be a simple but effective way to send emails. If you need a bit more scalability and extra features, but still want to use your own mail server, go-mail offers extra flexibility and functionality for more advanced email sending with features for good deliverability. But, if you want built-in features for activity tracking, advanced communication systems, email templating, and very high-volume sending, an email API would be the better fit.
2. How do I add file attachments to an email using Go?
How you attach files will depend on the package you are using. Net/smtp does not support attachments, but MailerSend and go-mail do.
3. How can my Go application know if an email was successfully delivered or bounced?
Net/smtp will only tell you if the server accepted the message. To know if the message was delivered or failed, you’ll need inbox monitoring or bounce parsing. Go-mail is similar in that it doesn’t include bounce tracking or offer webhooks, so you’d need to implement it yourself or set Return-Path to a monitored inbox. An API like MailerSend includes delivery tracking, so you can use webhooks to receive real-time notifications about whether the email has been delivered or bounced.
4. What are the sending limitations of using a personal email account (like Gmail) for an application?
For Gmail, there is a daily sending limit of 500 emails for personal accounts, and a limit of 100 recipients per email. If you exceed these limits, your account could be temporarily blocked or flagged for spam.
5. How do I use dynamic, pre-designed templates from MailerSend in my Go code?
Simply set the template ID for the message: message.SetTemplateID("template_ID") and ensure you’ve configured your personalization variables.
Get started with MailerSend for free
Sign up and start sending in minutes with our SDKs, developer documentation, and free testing domain.