As a modern Golang apps developer, you'll likely need to send emails from your code at some point, whether that's for user signups, password resets, notifications, whatever. The good news? Go makes this pretty straightforward, offering solid options for handling email via both SMTP and dedicated email APIs.
In this tutorial, I'll walk you through the different ways to send emails in Go using SMTP and Email APIs, with real code examples you can copy and paste to try out as you follow along.
What is Go and Why Use It for Email Sending
Go, also known as Golang, is an open-source programming language created by Google engineers Robert Griesemer, Rob Pike, and Ken Thompson in 2007. It was publicly released in 2009.
The language was designed to address shortcomings in existing programming languages while maintaining simplicity and efficiency.
Go offers several advantages that make it particularly suitable for email-sending applications:
- Performance and Efficiency: Go compiles to native machine code, delivering execution speeds comparable to C/C++ while maintaining memory safety through automatic garbage collection. This makes it great for high-throughput email systems that need to process thousands of messages quickly.
- Built-in Concurrency: Go's goroutines and channels provide lightweight concurrency primitives that excel at handling multiple email-sending operations simultaneously. A single Go program can efficiently manage thousands of concurrent email deliveries without the overhead of traditional threading models.
- Standard Library Support: The net/smtp package in Go's standard library provides native SMTP functionality, removing external dependencies for basic email operations.
- Excellent HTTP Support: Go's net/http package makes integrating with modern Email APIs straightforward, allowing developers to use advanced features like templates, analytics, and delivery tracking.
Understanding Email Sending Methods: SMTP vs Email API
Before talking about email functionality, let’s look at the two primary approaches available in Go: SMTP (Simple Mail Transfer Protocol) and Email APIs.
SMTP
SMTP is the standard Internet protocol for email transmission. When you send an email via SMTP in Go, your application connects directly to an SMTP server (either your own or a third-party’s) and transmits messages using the SMTP protocol.
Email API
Email APIs provide HTTP-based interfaces for sending emails. Instead of managing SMTP connections, you make HTTP requests to API endpoints with JSON payloads containing your email data.
For production applications, Email APIs typically offer better developer experience, automation, and operational features, while SMTP is used mostly for legacy applications and proof-of-concept scenarios.
Setting Up Your Go Development Environment
Before sending emails in Go, you need to install Go on your local machine and set up your development environment. If you already work with Go, you can skip this section.
Installing Go on Your System
Go is available on Windows, macOS, and Linux. Follow these steps for your operating system:
For Windows:
- Download the Windows installer (.msi file) from here.
- Run the installer and follow the prompts.
- Go will be installed to C:\Go by default.
- The installer automatically adds Go to your PATH.
For macOS:
- Download the .pkg installer from here.
- Run the package installer.
- Go will be installed to /usr/local/go.
- Add Go to your PATH by adding this line to ~/.bash_profile or ~/.zshrc:
|
export PATH=$PATH:/usr/local/go/bin |
- Apply the changes:
|
source ~/.bash_profile |
For Linux:
- Download the Linux tarball from here.
|
wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz |
- Remove any previous Go installation and extract the archive:
|
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz |
- Add Go to your PATH in ~/.profile or /etc/profile:
|
export PATH=$PATH:/usr/local/go/bin |
- Apply the changes:
|
source ~/.profile |
Whatever installation option you choose based on your system, run the go version command to verify your installation. You should get a response like this on your terminal:
Setting Up Your Project
Once Go is installed, create a new directory for your email project:
|
mkdir golang-email-tutorial |
Then, initialize a new Go module:
|
go mod init email-tutorial |
This creates a go.mod file that tracks your project's dependencies.
Sending Emails in Go Using SMTP
The SMTP approach gives you direct control over email transmission. We'll use the Gomail package for this tutorial due to its simplicity and powerful SMTP support.
Gomail is a Go package for sending emails, designed to be simple and efficient. It supports various email features like attachments, embedded images, HTML and text templates, SSL/TLS, and sending multiple emails over a single SMTP connection.
While it primarily sends emails via SMTP, its flexible interface makes it easy to integrate other methods, like using provider-specific APIs or local mail servers.
To get started with Gomail, install the package using the command below:
|
go get gopkg.in/mail.v2 |
For sending emails via SMTP, you also need an SMTP server. You may either use your own or the one provided by a dedicated service like UniOne.
Follow the steps below to get started with UniOne’s SMTP service:
- Sign up for a free UniOne account here.
- Add and verify your domain name. To learn more about this, refer to our guide on Setting up the domain’s DNS records. Alternatively, as a new user, you can use the free, pre-verified sandbox domain, which is a temporary domain for testing your email sending functionality before setting up your production domain.
- Navigate to Settings -> SMTP Configuration to get your SMTP credentials for sending emails.
Send Plain Text Email via SMTP
Create a file named smtp_plain.go in your directory with the following code. Insert your actual credentials and replace smtp.eu1.unione.io with the server you’ve registered at.
|
package main |
This code above shows how plain text emails are sent via SMTP using the gopkg.in/mail.v2 package. It creates a new message object, configures essential headers (sender, recipient, subject), sets the plain text body content, and establishes an SMTP connection using mail.NewDialer() with your credentials (host, port, username, and password).
It finally sends the email through DialAndSend(), which handles both the connection establishment and message transmission in a single operation, with error handling to catch and report any delivery failures.
Note: Replace the placeholder SMTP credentials in the code sample with your actual credentials obtained from the SMTP Configuration page in your UniOne dashboard.
To run this code:
|
go run smtp_plain.go |
You should get an email sent confirmation in your terminal like this:
When you check the inbox of the email specified in To, you should see a message like this:
Pretty cool, right? :)
Send HTML Email via SMTP
HTML emails provide rich formatting capabilities. Here's how to send HTML emails while maintaining a plain text fallback:
|
package main |
Always include both plain text and HTML versions using SetBody() for plain text and AddAlternative() for HTML. Email clients that cannot render HTML will display the plain text version. In addition, HTML-only emails are often despised by spam filters.
When you run the code, you should get an email in your inbox like this:
Send Email to Multiple Recipients via SMTP
Sending to multiple recipients requires adding multiple email addresses to the "To" header:
|
package main |
The SetHeader("To", ...) method accepts multiple email addresses as variadic arguments, while SetHeader("Cc", ...) and SetHeader("Bcc", ...) allow you to add carbon copy and blind carbon copy recipients, respectively. Once configured, the DialAndSend() method sends one email that reaches all specified recipients in their respective recipient categories (To, CC, or BCC).
An important note: in UniOne’s SMTP service, these headers are treated in a non-standard way. By default, it sends each recipient from the “To” and “CC” headers their own copy of the letter with a single email address in the final “To” header. To revert to the standard behaviour, you must enable strict mode by setting the strict parameter to true in the X-UNIONE header like this: X-UNIONE: {"strict":true}, or contact technical support to enable this mode for all your emails. Also, the “To” header must not be empty even if CC addresses are present.
Send Email with Attachments via SMTP
Attachments are the way you and I send images, documents, invoices, or reports via email. Gomail handles file attachments seamlessly:
|
package main |
The Attach() method automatically handles MIME encoding and sets appropriate content types based on file extensions.
Run the code, and you should get an email like this:
Send Email with Embedded Images via SMTP
Embedded images display inline within the email body, providing a better user experience than attachments:
|
package main |
Reference embedded images in HTML using cid:filename.ext, where filename.ext matches the embedded file name.
Send Emails Asynchronously via SMTP
For applications that send multiple emails, asynchronous sending prevents blocking your main application flow. Go's goroutines make this straightforward:
|
package main |
Code Explanation:
- sync.WaitGroup tracks all goroutines to ensure the program waits for completion;
- Each email is sent in a separate goroutine using the go keyword;
- defer wg.Done() decrements the wait group counter when the function completes;
- wg.Wait() blocks execution until all emails are sent.
Send Bulk Emails via SMTP
Bulk email sending requires rate limiting, or throttling, to avoid overwhelming SMTP servers or triggering spam filters. With some SMTP providers, including UniOne, you do not need to implement throttling by yourself – the provider queues all your messages and takes care of all sending limits, which, by the way, may be different for different target domains. If you still need to add throttling capability to your code, follow this example:
|
package main |
The time.Tick() function creates a channel that sends a value at specified intervals, effectively limiting email sending to one per second in this example. Adjust the interval based on your SMTP provider's limits.
Sending Emails in Go Using Email API
Email APIs provide a modern, HTTP-based approach to sending emails with enhanced features like templates, analytics, and webhook notifications. This section demonstrates how to integrate UniOne's Email API with your Go application.
UniOne's Email API uses HTTPS POST requests with JSON payloads. Be sure to specify the correct hostname:
- US/Canada: https://us1.unione.io/en/transactional/api/v1
- Europe: https://eu1.unione.io/en/transactional/api/v1
Authentication is handled via API key in the X-API-KEY HTTP header. You can obtain your API key from your dashboard by navigating to Account -> Security -> API key.
Send Plain Text Email via Email API
Create a file named api_plain.go and enter the code below:
|
package main |
- The job_id returned in the response can be used to track email delivery status.
- Failed emails are returned in the failed_emails object with rejection reasons.
- Maximum request size is 10 MB.
When you run the code, you should get an output in your terminal like this:
When you check the email, you should see this:
Send HTML Email via Email API
HTML emails require both html and plaintext fields in the body:
|
// Use the same struct definitions from the previous example |
- track_links: 1 – logs an event when recipients click links in your email
- track_read: 1 – logs an event when recipients open your email
Tracking data is available via webhooks or the UniOne dashboard.
Send Email to Multiple Recipients via Email API
The UniOne API supports up to 500 recipients per request:
|
emailReq := EmailRequest{ |
- Each recipient can have unique substitution values.
- Use {{variable_name}} syntax in your content.
- Supports simple, velocity, or liquid template engines.
- Variables work in subject, html, plaintext, and from_name fields.
Send Email with Attachments via Email API
Attachments must be base64-encoded. UniOne supports attachments up to 7MB each:
|
// Attachment represents a file attachment |
Important notes:
- Maximum attachment size is about 7MB (9,786,710 bytes when base64-encoded; keep request size below 10Mb).
- The / (forward slash) symbol is not allowed in attachment names.
- Common MIME types are: application/pdf, image/jpeg, image/png, text/plain, application/vnd.ms-excel.
Send Emails Asynchronously via Email API
Use goroutines for concurrent API requests, just like with SMTP:
|
package main |
Send Bulk Emails via Email API
For bulk sending, use batching with the 500-recipient limit and implement rate limiting, if necessary. Again, with UniOne you do not need to care about the latter – the service does it for you, and in a more sophisticated way.
|
package main |
Best Practices for Bulk Sending:
- Batch recipients in groups of 500 (API maximum).
- Implement rate limiting to avoid overwhelming the API.
- Use substitutions for personalization.
- Handle failed emails in the response.
- Monitor delivery via webhooks for real-time status updates.
Go Libraries for Email Sending
Several libraries are available for sending emails in Go, each with different features and use cases.
Gomail
Gomail (which was used in this tutorial) is the most popular third-party library for email sending in Go. It provides a clean, intuitive API and handles MIME encoding automatically.
Key Features:
- Simple API for email composition;
- Automatic handling of attachments and embedded images;
- Support for SSL/TLS encryption;
- Text and HTML email support;
- Special character encoding.
net/smtp Package
The net/smtp package is Go's standard library solution for SMTP communication. While it provides basic functionality, it lacks features like attachment handling and HTML email support.
Key Features:
- No external dependencies;
- Direct SMTP protocol implementation;
- Plain authentication and CRAM-MD5 authentication;
- Low-level control over SMTP operations.
Limitations:
- No built-in MIME attachment support;
- Manual header construction required;
- Limited to basic email functionality;
- Frozen package (no new features being added).
It is great for simple notification systems or when external dependencies must be minimized.
Here’s a basic example of this package’s implementation:
|
package main |
mail Package
The mail package provides another alternative with features like attachment support and connection pooling.
Great for applications needing connection pooling for high-volume sending.
Best Practices for Sending Emails in Go
Implementing email functionality correctly requires attention to deliverability, security, and performance considerations.
Here are some best practices to consider during implementation.
- Use TLS Encryption: Always enable TLS when connecting to SMTP servers to encrypt credentials and email content:
|
dialer := mail.NewDialer("smtp.example.com", 587, "username", "password") |
- Store Credentials Securely: Never hardcode API keys or passwords in source code. Use environment variables:
|
import "os" |
- Implement SPF, DKIM, and DMARC: Configure these email authentication protocols in your DNS records to improve deliverability and prevent spoofing. Most email service providers like UniOne handle DKIM signing automatically.
Conclusion
Go provides powerful capabilities for email sending through both SMTP and modern Email APIs. The choice between these approaches depends on your specific requirements.
Choose SMTP when you need compatibility with legacy code, or want to avoid vendor lock-in. The Gomail library offers the best balance of features and ease of use for SMTP-based sending.
Choose Email APIs when you need top speed and advanced features like delivery analytics, template management, or simplified integration. APIs handle complex tasks like MIME encoding automatically, provide better error reporting and offer event tracking.
For production applications, consider using established email service providers like UniOne, which usually offer both SMTP service and Email API options with features like deliverability optimization, dedicated IPs, and comprehensive analytics.
Remember to implement proper error handling, respect rate limits, maintain sender reputation, and follow email authentication best practices to ensure your emails reach recipients' inboxes reliably.
Related Services
For production-grade email delivery in your Go applications, consider these UniOne services:
- SMTP Service – Reliable SMTP infrastructure with advanced features like variable substitution, template management, and detailed analytics.
- Email API – Developer-friendly HTTP API for transactional and marketing emails with real-time event tracking.
- Email Testing – Tools to debug your SMTP session before sending at scale.
FAQ
How do I handle bounced emails in Go?
Implement webhook endpoints to receive bounce notifications from your email provider. Most services, including UniOne, provide real-time event webhooks that notify your application of bounces, spam complaints, and other delivery events. Store these events in your database and maintain suppression lists to avoid sending to unreachable addresses.
What's the difference between using net/smtp and Gomail?
The net/smtp package is Go's standard library SMTP implementation, providing basic functionality but requiring manual MIME encoding for attachments and HTML emails. Gomail is a third-party library that simplifies email creation by automatically handling attachments, embedded images, and proper MIME encoding. For production applications, Gomail offers significantly better developer experience.
Do I need a dedicated IP address for sending emails from Go?
For transactional emails with low to moderate volume, shared IPs provided by email services work quite well. Dedicated IPs become important when sending large volumes (100,000+ monthly emails) or when you need complete control over your sender reputation. Dedicated IPs allow you to isolate your sending reputation from other users and are highly recommended for established businesses with consistent sending patterns.