Guide
How to send email with Go
Send email from Go with net/http and the REST API.
In short
Create a Sendpository account, verify your domain, put your API key inSENDPOSITORY_API_KEY, and send with the code below - one HTTPS request per email, from server-side code only.1. Verify your sending domain
Add your domain in the dashboard and publish the three DNS records it shows - SPF and a bounce record on an sp. subdomain, and a DKIM key on your apex. Your existing records are never edited. Verification usually lands in a few minutes, and you can set up the code below while you wait.
2. Create a key
Create an API key with the sending permission. It is shown once, so put it straight into your secrets. Read the key with os.Getenv("SENDPOSITORY_API_KEY").
3. Send an email
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
func main() {
body, _ := json.Marshal(map[string]any{
"from": "Your App <hello@mail.yourdomain.com>",
"to": []string{"customer@example.com"},
"subject": "Your receipt",
"html": "<p>Thanks for your order.</p>",
})
req, _ := http.NewRequest("POST", "https://api.sendpository.com/v1/emails", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SENDPOSITORY_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var out map[string]any
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(res.StatusCode, out)
}A success returns { "id": "..." } - the message’s id for looking it up later. A failure returns { "error": { "type", "message" } } with a 4xx or 5xx status.
Context and goroutines
Pass the request's `context.Context` into the call so a cancelled request cancels the send with it. A goroutine started with `context.Background()` outlives the request and keeps running after the client has gone.
If the caller should not wait, start a goroutine with a fresh context carrying its own timeout - never the request context, which is cancelled the moment the handler returns.
Testing it
- Reuse one `http.Client` package-wide. A new client per call means a new connection pool and no keep-alive.
- `defer resp.Body.Close()` on every response, including error paths, or the connection is never returned to the pool.
Things to know
- Use a client with a Timeout - http.DefaultClient has none.
- Decode into a struct with an Error field in production code to branch on error.type.
Questions
Which context should I pass?
The request's context if the caller is waiting, so a cancelled request cancels the send too. If you are sending in the background, start a fresh context with its own timeout - the request context is cancelled the moment the handler returns, which would kill the send.
How do I send email with Go?
Verify your sending domain, create an API key, put it in an environment variable, and POST the message as JSON to https://api.sendpository.com/v1/emails from server-side Go code. The example on this page runs as written once the domain is verified.
Can I send email with Go without SMTP?
Yes. This is plain HTTPS, so Go needs no mail library and no SMTP port - which matters on hosts that block outbound 25 and 587. Failures come back as a status code on the call instead of as a bounce message hours later.
More guides
Send email with Node.js
Send email from any Node.js app with the typed SDK or plain fetch.
Send email with Python
Send email from Python with requests and the REST API.
Send your first email in minutes
6,000 emails a month (up to 200 a day), free forever. No card needed.