Home/Email API/Node.js send email

Node.js send email

Node.js sends email through a library that speaks SMTP, most commonly Nodemailer. There is no mail transport in the standard library, so this is a dependency rather than a built-in.

VerifiedBy Kam Low, Co-founder·Updated

Install the library

Nodemailer installs from npm and has no runtime dependencies of its own, which is part of why it became the default choice.

Verified against npm
zero runtime dependencies Node 18+ fully typed
The entry-point split
nitrosendOne package
root exportserver only, holds the secret key
nitrosend/browserthe export meant for client code
Dependencies, runtime, types: table stakes. The export split is what keeps the secret key off the client.

It supports both SMTP and a pluggable transport interface, so the same calling code can target a provider's HTTP API through a transport plugin instead of SMTP.

Implementation

The working sequence is two steps. Create a transporter with the host, port, security flag and authentication object. Call sendMail with an options object holding from, to, subject, and either text or html.

The secure flag is the setting people get wrong. Set to true it opens an implicit TLS connection, which pairs with port 465. The false value instead starts in the clear and upgrades through STARTTLS, which is the port 587 path. Pairing secure: true with port 587 produces a connection that hangs rather than returning a usable error.

Set both text and html when sending HTML. Nodemailer assembles the multipart message, and the plain-text part is what some clients render and what filters treat more favorably.

sendMail returns a promise resolving to an info object containing the message ID and the server's accepted and rejected recipient arrays. A send can resolve successfully while individual recipients were rejected, so checking info.rejected matters when sending to more than one address.

Reuse a transporter across sends rather than creating one per message. Each new transporter opens a fresh connection, and providers limit concurrent connections per account.

Run the script

Running the file executes the send. Because sendMail is asynchronous, an unhandled rejection exits silently in older Node versions, so wrap the call in try/catch or attach a catch handler.

Credentials belong in environment variables rather than the source file. A committed application password is a live sending credential for the mailbox that issued it.

Common provider configuration

Gmail uses smtp.gmail.com on port 587 with STARTTLS or 465 with implicit TLS. Google blocks ordinary account passwords for third-party clients, so an app password generated against an account with two-step verification is required.

Microsoft 365 uses smtp.office365.com on port 587 with STARTTLS, documented in Microsoft's settings reference. Basic authentication on this path is scheduled for deprecation in favor of OAuth, which Nodemailer supports through an OAuth2 auth object.

Both are mailbox providers, and both apply per-mailbox sending limits sized for a person rather than an application. Microsoft documents 10,000 recipients per day and 30 messages per minute on client submission.

For application volume the alternative is a submission service authenticating as your own verified domain, reached either over SMTP with issued credentials or through an HTTP API. Nitrosend's integration surface is a REST API, an MCP server, a CLI and a typed Node SDK, @nitrosend/sdk, so integration there is a package install or an ordinary HTTP request.

The official package, and the split that matters

There is one official package for Node and TypeScript, and it is the exception to the REST-only rule that applies to every other language. It is fully typed, ships with zero runtime dependencies, and requires Node 18 or later.

Zero dependencies is a deliberate property rather than a boast. An email SDK sitting in a dependency tree with a dozen transitive packages is a supply-chain surface attached to a credential that can send as your domain, so the smaller that surface the better.

Most consequential is the split between server and browser entry points. The main export is a server SDK for Node, edge functions or any backend, and it takes a secret key with full account access. The package also publishes a separate browser entry, and the distinction is not cosmetic: a secret key must never reach a browser bundle, and having two entry points is what makes that boundary explicit rather than a matter of discipline.

Worth knowing that raw HTTP is still a first-class option. Our own REST guide's JavaScript examples use plain fetch, and there is nothing second-class about that: the package calls the same API, and the same key authenticates both, so the choice is about typing convenience rather than capability.

None of that split, the typed errors, the zero dependencies, is really the point. The point is you don't have to write any of it. Tell Claude, Cursor, or Codex to send the email, and our MCP server composes it, picks the transport, and sends, with you approving before anything leaves. Email marketing is moving from developers wiring up SDKs to agents doing the wiring, and Nitrosend is the option built for that shift, which is why we're a listed plugin in ChatGPT's plugin directory rather than just another package on npm.

Go deeper

Email API

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.

Choose your billing interval

Every paid plan includes 3 agent inboxes and no Nitrosend footer.

Starter

For your first email program.

$16USD / month

$192 billed yearly.

20,000 emails / month

Choose Starter
  • 5 brands · 3 seats
  • 5 custom sending domains
  • 500 email verifications/month
  • $1.50 / 1,000 extra emails

Pro

For a growing audience.

$40USD / month

$480 billed yearly.

60,000 emails / month

Choose Pro
  • 10 brands · 5 seats
  • 10 custom sending domains
  • 1,000 email verifications/month
  • $1.20 / 1,000 extra emails

Growth

For teams and agencies.

$120USD / month

$1,440 billed yearly.

200,000 emails / month

Choose Growth
  • Unlimited brands · 10 seats
  • 25 custom sending domains
  • 2,500 email verifications/month
  • $0.90 / 1,000 extra emails

Scale

For high-volume sending.

$240USD / month

$2,880 billed yearly.

500,000 emails / month

Choose Scale
  • Unlimited brands · 20 seats
  • 100 custom sending domains
  • 5,000 email verifications/month
  • $0.70 / 1,000 extra emails
Enterprise

Hey, big sender.

Sending 500,000+ emails a month? Get a custom plan designed for you, with volume email pricing and capacity that fits your business.

Start free. No card needed.

5,000 emails to start, then 500 emails every month. No expiry on your starter emails.

Start free

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 forever. No credit card required. See full comparison →