Send email with Go

net/smtp is a faithful implementation of a 1982 protocol. Your Go service deserves one stdlib http.Post instead.

Verified By George Hartley, Co-founder · Updated July 20, 2026

net/smtp is 1982

Go's net/smtp does exactly what the RFC says and nothing more: no HTML helpers, no auth beyond PLAIN and CRAM-MD5, and the package is frozen by design. The Go team themselves point users elsewhere.

Elsewhere is one stdlib HTTP call.

One POST, and the send is handled

GoPOST /v1/my/messages
body := `{"from":"[email protected]","to":"[email protected]",
  "subject":"Welcome","html":"<p>You're in.</p>"}`

req, _ := http.NewRequest("POST",
  "https://api.nitrosend.com/v1/my/messages",
  strings.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("NITROSEND_API_KEY"))
http.DefaultClient.Do(req)

No third-party module, no SMTP dialer, no TLS negotiation to reason about. The response carries the message ID; your webhook carries the rest.

And the infrastructure behind that POST is sized like a systems engineer would want: we run on a million-a-day sending limit as a baseline, with five million a day available on request. Your Goroutines will not be the bottleneck, and neither will we.

The API design philosophy is fail-closed, which Go engineers will appreciate. We once found a misplaced scheduled_at field being silently dropped, which meant an immediate send instead of a scheduled one: a 24,000-email misfire waiting to happen. Now a wrong-shaped field fails closed with a 422 pointing at the correct one. Silent coercion is how footguns ship. The rule is a hardcoded invariant in the codebase now: audience resolution fails closed, never open.

Full parameters, responses, and error codes live in the REST API docs and the API reference.

Python Node.js PHP Go Rust +5
send.py requests ยท stdlib-adjacent
import requests

requests.post(
  "https://api.nitrosend.com/v1/messages",
  headers={"Authorization": f"Bearer {KEY}"},
  json={"to": to, "subject": subj,
        "html": body})
202 ACCEPTED
{ "id": "msg_9f2c",
  "status": "accepted" }

And the part no library gives you

The same account exposes an MCP server, so the agent writing your code can also run your email. Point Claude, Cursor, or Codex at api.nitrosend.com/mcp and it composes, previews, and stages sends, with a human approving every one. The API is for your code. The MCP is for your agent. One platform either way.

Every send comes back with a message ID, and delivery events follow on your webhook: delivered, opened, bounced. No dashboard required to know what happened.

Go deeper

Guides

First send in thirty seconds.

Simple pricing. Unlimited contacts.

Every plan includes full stack emailing: Flows, Newsletter Campaigns and Transactional Email, plus our NitroWheel LLM and all agent integrations (Claude, ChatGPT, Codex, Cursor and others). Pay for what you send, not who you store.

Free
$0
forever
  • Emails 8,000then 500/mo
  • Email types Transactional & Marketing
  • AI actions 20/mo
  • Contacts Free & Unlimited
  • Brands 1
  • Seats 1
Start free
Ultra
$100
per month
  • Emails 125,000/month
  • AI actions 5,000/mo
  • Brands 5
  • Seats 10 · Domains 3
  • Frontier AI Included
  • Dedicated IP Available
Get started
Enterprise
$300
per month
  • AI actions Unlimited
  • Unlimited brands & domains Included
  • SSO / SAML Included
  • 99.9% SLA Included
Get started

Free forever. No credit card required. See full comparison →