SDKs
Official MillionSend SDKs for nine languages, published under MIT.
Every SDK mirrors the shape of the corresponding official Resend SDK, so migrating is mostly a find-and-replace: swap the import/class and point the base URL at MillionSend. All SDKs are MIT-licensed and live in their own repositories under the MillionSend GitHub org.
Since the API is Resend wire-compatible, the official Resend SDKs also work —
point them at MillionSend via their base-URL option (e.g. RESEND_BASE_URL).
From 0.9.0, every MillionSend SDK also covers
received mail and inboxes.
Base URL
api.millionsend.com — what every example on this page uses, and the default from SDK 0.5.0: with no base URL configured, an SDK talks to Cloud.
Node.js / TypeScript
millionsend on npm — Node 18+.
npm install millionsendimport { MillionSend } from "millionsend";
const ms = new MillionSend("ms_123", { baseUrl: "https://api.millionsend.com" });
const { data, error } = await ms.emails.send({
from: "Acme <onboarding@acme.dev>",
to: "success@simulator.amazonses.com",
subject: "Hello from MillionSend",
html: "<strong>It works!</strong>",
});Python
millionsend on PyPI — Python 3.9+.
pip install millionsendimport millionsend
millionsend.api_key = "ms_123"
millionsend.base_url = "https://api.millionsend.com"
email = millionsend.Emails.send({
"from": "Acme <onboarding@acme.dev>",
"to": "success@simulator.amazonses.com",
"subject": "Hello from MillionSend",
"html": "<strong>It works!</strong>",
})Go
millionsend-go — Go 1.21+.
go get github.com/MillionSend/millionsend-goclient := millionsend.NewClient("ms_123")
client.BaseURL = "https://api.millionsend.com"
sent, err := client.Emails.Send(&millionsend.SendEmailRequest{
From: "Acme <onboarding@acme.dev>",
To: []string{"success@simulator.amazonses.com"},
Subject: "Hello from MillionSend",
Html: "<strong>It works!</strong>",
})Ruby
millionsend on RubyGems — Ruby 3.0+.
gem install millionsendrequire "millionsend"
Millionsend.api_key = "ms_123"
Millionsend.base_url = "https://api.millionsend.com"
email = Millionsend::Emails.send(
from: "Acme <onboarding@acme.dev>",
to: "success@simulator.amazonses.com",
subject: "Hello from MillionSend",
html: "<strong>It works!</strong>"
)PHP
millionsend/millionsend-php on Packagist — PHP 8.1+.
composer require millionsend/millionsend-phpuse MillionSend\MillionSend;
$ms = MillionSend::client('ms_123', 'https://api.millionsend.com');
$email = $ms->emails->send([
'from' => 'Acme <onboarding@acme.dev>',
'to' => 'success@simulator.amazonses.com',
'subject' => 'Hello from MillionSend',
'html' => '<strong>It works!</strong>',
]);Rust
millionsend on crates.io — async
(tokio + reqwest).
[dependencies]
millionsend = "0.9"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use millionsend::{MillionSend, SendEmailOptions};
let ms = MillionSend::with_base_url("ms_123", "https://api.millionsend.com");
let sent = ms.emails.send(&SendEmailOptions {
from: "Acme <onboarding@acme.dev>".into(),
to: "success@simulator.amazonses.com".into(),
subject: "Hello from MillionSend".into(),
html: Some("<strong>It works!</strong>".into()),
..Default::default()
}).await?;Java
com.millionsend:millionsend-java on Maven Central — Java 11+.
<dependency>
<groupId>com.millionsend</groupId>
<artifactId>millionsend-java</artifactId>
<version>0.9.0</version>
</dependency>MillionSend ms = new MillionSend("ms_123", "https://api.millionsend.com");
CreateEmailResponse sent = ms.emails().send(
CreateEmailOptions.builder()
.from("Acme <onboarding@acme.dev>")
.to("success@simulator.amazonses.com")
.subject("Hello from MillionSend")
.html("<strong>It works!</strong>")
.build());.NET
MillionSend on NuGet — targets net8.0.
dotnet add package MillionSendusing MillionSend;
var client = new MillionSendClient("ms_123", "https://api.millionsend.com");
var res = await client.EmailSendAsync(new EmailMessage
{
From = "Acme <onboarding@acme.dev>",
To = "success@simulator.amazonses.com",
Subject = "Hello from MillionSend",
Html = "<strong>It works!</strong>",
});Elixir
millionsend on Hex — Elixir 1.15+.
# mix.exs
def deps do
[{:millionsend, "~> 0.9"}]
endconfig :millionsend, MillionSend.Client,
api_key: System.get_env("MILLIONSEND_API_KEY"),
base_url: "https://api.millionsend.com"
{:ok, email} =
MillionSend.Emails.send(%{
from: "Acme <onboarding@acme.dev>",
to: "success@simulator.amazonses.com",
subject: "Hello from MillionSend",
html: "<strong>It works!</strong>"
})Receiving and inboxes
From 0.9.0, every SDK reads received mail and works with inboxes, with the same tree in each language's idiom (the names below are Node's):
| Resource | Methods |
|---|---|
emails.receiving | list (with wait, since, inboxId, to, from, subjectContains, unread, includeQuarantined), get, reply, forward, remove |
emails.receiving.attachments | list, get |
inboxes | create, list, get (by id or address), update, remove |
inboxes.safety | get, update |
inboxes.codes | latest (with from, wait, maxAge) |
inboxes.quarantine | list, release (with trustSender) |
inboxes.labels | list, create, update, remove |
inboxes.threads | list, search, get, update, remove |
inboxes.threads.emails | list, get, reply, forward |
inboxes.drafts | list, create, get, update, remove, send, reject |
import { MillionSend } from "millionsend";
const ms = new MillionSend("ms_123");
// Wait up to 30 seconds for the next email delivered to an inbox.
const { data: page } = await ms.emails.receiving.list({ inboxId: "7eeb9517-aed7-40d7-ad65-500be42ed147", wait: 30 });
// The newest verification code or sign-in link it received.
const { data: code } = await ms.inboxes.codes.latest("7eeb9517-aed7-40d7-ad65-500be42ed147", { wait: 30 });- Reply, forward and draft sends take an idempotency key (in Node
{ idempotencyKey }, in GoWithIdempotencyKey, and each language's equivalent). Pass one to make a retry safe: the SDK sends none on its own. - A reply or forward the inbox's rules hold answers a draft instead of the
sent email, and the SDKs say which: in Node the result carries
kind: "sent"orkind: "draft", and each language has its equivalent. - Unknown fields and enum values are kept or ignored, never an error, so a newer API doesn't break an older SDK.
- Domains gain
receiving_catch_all, API keysinbox_id, webhooksinbox_ids, and the webhook event typesemail.receivedand the inbox events.