Build a list, add contacts, and send a broadcast to your whole audience from the dashboard or the API — draft it, schedule it, or send now. Watch delivery progress live and retry only the recipients who failed, from the same domain and quota as your transactional mail.
Create a list, add or import contacts, and target a broadcast at it — no separate marketing-email vendor or contact sync to maintain.
A broadcast is a draft until you're ready — send immediately or schedule it for later, and cancel a scheduled send to edit it right up until it goes out.
Sends drain gradually with a live audience count and status, so you can watch a broadcast go out instead of firing it blind.
If some recipients fail, retry just that subset — the rest of the audience isn't re-sent to.
Make a list in the dashboard (or via the API) and add contacts one at a time or in bulk.
Compose the subject and body, pick the list as the audience, and save it as a draft.
Send immediately, or schedule a time — watch audience count, delivery status, and failures live once it's sending.
Lists, contacts, and the broadcast itself are all the same API you already send transactional mail through.
Dashboard → Lists → [ + New list ] "Product updates"
Add contacts (or import a CSV)
Dashboard → Broadcasts → [ + New broadcast ]
From MyApp <updates@myapp.ai>
Subject What shipped this week
Audience List → Product updates
Body Three new things landed…
Click [ Send now ] (or [ Schedule ])Create a list called "Product updates", then draft a broadcast from
updates@myapp.ai with subject "What shipped this week" to that list
and send it.import { MailKite } from "mailkite";
const mk = new MailKite(process.env.MAILKITE_API_KEY);
const list = await mk.createList({ name: "Product updates" });
await mk.addListContacts(list.id, { contactIds: ["ctr_ada…", "ctr_grace…"] });
const broadcast = await mk.createBroadcast({
from: "MyApp <updates@myapp.ai>",
subject: "What shipped this week",
audience: { type: "list", id: list.id },
html: "<p>Three new things landed…</p>",
});
// Pass { scheduledAt } to send it later instead.
await mk.sendBroadcast(broadcast.id, {});import os
from mailkite import MailKite
mk = MailKite(os.environ["MAILKITE_API_KEY"])
lst = mk.createList({"name": "Product updates"})
mk.addListContacts(lst["id"], {"contactIds": ["ctr_ada…", "ctr_grace…"]})
broadcast = mk.createBroadcast({
"from": "MyApp <updates@myapp.ai>",
"subject": "What shipped this week",
"audience": {"type": "list", "id": lst["id"]},
"html": "<p>Three new things landed…</p>",
})
# Pass {"scheduledAt": …} to send it later instead.
mk.sendBroadcast(broadcast["id"], {})<?php
$mk = new \MailKite\Client(getenv('MAILKITE_API_KEY'));
$list = $mk->createList(['name' => 'Product updates']);
$mk->addListContacts($list['id'], ['contactIds' => ['ctr_ada…', 'ctr_grace…']]);
$broadcast = $mk->createBroadcast([
'from' => 'MyApp <updates@myapp.ai>',
'subject' => 'What shipped this week',
'audience' => ['type' => 'list', 'id' => $list['id']],
'html' => '<p>Three new things landed…</p>',
]);
// Pass ['scheduledAt' => …] to send it later instead.
$mk->sendBroadcast($broadcast['id'], []);MailKite mk = new MailKite(System.getenv("MAILKITE_API_KEY"));
Map<String, Object> list = (Map<String, Object>) mk.createList(Map.of("name", "Product updates"));
mk.addListContacts((String) list.get("id"), Map.of("contactIds", List.of("ctr_ada…", "ctr_grace…")));
Map<String, Object> broadcast = (Map<String, Object>) mk.createBroadcast(Map.of(
"from", "MyApp <updates@myapp.ai>",
"subject", "What shipped this week",
"audience", Map.of("type", "list", "id", list.get("id")),
"html", "<p>Three new things landed…</p>"
));
// Pass Map.of("scheduledAt", …) to send it later instead.
mk.sendBroadcast((String) broadcast.get("id"), Map.of());mk := mailkite.New(os.Getenv("MAILKITE_API_KEY"))
list, _ := mk.CreateList(map[string]any{"name": "Product updates"})
listID := list.(map[string]any)["id"].(string)
mk.AddListContacts(listID, map[string]any{
"contactIds": []string{"ctr_ada…", "ctr_grace…"},
})
broadcast, _ := mk.CreateBroadcast(map[string]any{
"from": "MyApp <updates@myapp.ai>",
"subject": "What shipped this week",
"audience": map[string]any{"type": "list", "id": listID},
"html": "<p>Three new things landed…</p>",
})
// Pass {"scheduledAt": …} to send it later instead.
_, err := mk.SendBroadcast(broadcast.(map[string]any)["id"].(string), map[string]any{})require "mailkite"
mk = Mailkite::Client.new(ENV["MAILKITE_API_KEY"])
list = mk.createList("name" => "Product updates")
mk.addListContacts(list["id"], "contactIds" => ["ctr_ada…", "ctr_grace…"])
broadcast = mk.createBroadcast(
"from" => "MyApp <updates@myapp.ai>",
"subject" => "What shipped this week",
"audience" => { "type" => "list", "id" => list["id"] },
"html" => "<p>Three new things landed…</p>"
)
# Pass "scheduledAt" => … to send it later instead.
mk.sendBroadcast(broadcast["id"], {})# 1. A list to send to.
curl https://api.mailkite.dev/api/lists \
-H "Authorization: Bearer $MAILKITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Product updates" }'
# 2. Contacts on it.
curl https://api.mailkite.dev/api/lists/lst_…/contacts \
-H "Authorization: Bearer $MAILKITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "contactIds": ["ctr_ada…", "ctr_grace…"] }'
# 3. The broadcast itself.
curl https://api.mailkite.dev/api/broadcasts \
-H "Authorization: Bearer $MAILKITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "MyApp <updates@myapp.ai>",
"subject": "What shipped this week",
"audience": { "type": "list", "id": "lst_…" },
"html": "<p>Three new things landed…</p>"
}'
# 4. Send it — add "scheduledAt" to send later instead.
curl -X POST https://api.mailkite.dev/api/broadcasts/bct_…/send \
-H "Authorization: Bearer $MAILKITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}' It covers lists, contacts, and scheduled or on-demand broadcast sends on your own domain. If you need deep marketing automation (drip campaigns, complex segmentation), evaluate against your specific needs — MailKite's broadcasts are built for straightforward product updates and newsletters.
Broadcasts send from the same domain, so keep your audience opted-in and engaged — that's good practice for any sender. Ask about separated sending streams if you're running high-volume campaigns alongside critical transactional mail.
Yes — a scheduled broadcast can be canceled, which returns it to a draft you can edit and reschedule.
Start free on unlimited domains — no credit card. Or browse the other solutions.