Home/Email API/Node.js

Send email with Node.js

One fetch call beats a Nodemailer config. Sending email from Node.js is one authenticated HTTP request, or one SDK call if you prefer it typed.

Verified By Kam Low, Co-founder · Updated

Nodemailer wants config. Fetch wants a URL.

Nodemailer is the default answer, and it front-loads the work: a transporter, an SMTP host, a port, auth credentials, and a connection to keep healthy. All of that before the first send, and none of it helps the email land.

The API path is the fetch call Node already ships with, or the SDK when you want types and defaults.

One POST, and the send is handled

Node.jsPOST /v1/my/messages
import { Nitrosend } from "@nitrosend/sdk"
const ns = new Nitrosend(process.env.NITROSEND_API_KEY)

await ns.messages.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Welcome",
  html: "<p>You're in.</p>",
})

npm install @nitrosend/sdk, one env var, one call. Prefer zero dependencies? The same send is a plain fetch POST to /v1/my/messages. Either way you get a message ID back before the response settles into your logs.

And the agent path is one line too: claude mcp add nitrosend -- npx -y @nitrosend/mcp. Both packages live under the nitrosend org on npm, @nitrosend/sdk and @nitrosend/mcp, alongside a full OpenAPI spec at api.nitrosend.com/openapi.yaml that we keep validated on every API change. The npm ecosystem your project lives in is the ecosystem we ship in.

The SDK is built the way Node people want it built: fully typed, zero dependencies, Node 18 and up, with typed errors you catch with instanceof rather than string-matching a message. And if you're wondering who else is on the other end of these calls: about half our client traffic is Claude in one form or another, with ChatGPT, Cursor, curl, n8n, and plain Node filling out the rest. Node is not an afterthought SDK for us. It's the front door.

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. If you are staying on SMTP, how to set up SMTP is the setup to follow, and SPF, DKIM and DMARC authentication is what stops those sends landing in spam.

Sending a single newsletter to a large list is a solved problem. Generating unique content for every recipient breaks most infrastructure. George, our co-founder, detailed this scaling wall on Developer Podcast. The real hurdle is "creating half a million different emails with different content" without crashing your servers. We built our API to absorb that volume. Your Node application just makes the request and moves on.

Sending a static newsletter to a huge list is a solved problem. Personalizing every message at volume breaks most backend setups. George, our co-founder, lived this reality on Developer Podcast. A basic script cannot survive "creating half a million different emails with different content" concurrently. The API takes on that rendering load automatically. You just make the fetch request and move on to the next task.

Every choice on this page, Nodemailer or fetch, SDK or raw HTTP, is still a developer's call. The MCP path removes the call. Claude, Cursor, or Codex read the account, compose the send, and ask you to approve it before it leaves, no transporter config required. It's why Nitrosend shows up as a listed app inside the ChatGPT marketplace instead of just another npm package.

Go deeper

FAQ

How do I send email with Node.js?

Install @nitrosend/sdk and call ns.messages.send({ from, to, subject, html }), or make a plain fetch POST to the REST endpoint with a bearer token. Message ID back, delivery events on your webhook.

Do I need Nodemailer?

Only for stacks that must speak SMTP. For everything else, one HTTP call replaces the transporter config, and the platform handles authentication, deliverability, and tracking.

Can my AI agent send email from my Node project?

Yes. The same account exposes an MCP server, so Claude, Cursor, or Codex can compose and stage sends while you approve each one.

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.

Daily allowances depend on your plan and sender standing. Strong list, domain and delivery evidence can raise standing, including on day one. Trusted receives the full plan allowance; available email credits, safety checks and delivery pacing still apply.

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

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