Best API-First Email Services in 2026
To build lifecycle email on an API you have two designs to choose from. A pure sending API (Postmark, Amazon SES, Mailgun, SMTP2GO) delivers each message you request and leaves scheduling, state and exit rules to your code. An event API (Resend Automations, Loops, Customer.io) lets your code report what a user did and runs the sequence for you. Resend is the strongest single-account pick because one key covers both modes; Loops and Customer.io are the dedicated event-driven options; SES is the cheapest sender if you accept building the rest.
Endpoints, SDK lists and limits below were read from each vendor's documentation, and prices from its pricing page, on 7 October 2026. Every vendor section links the pages used.
Which API fits which sequence job
Six things an email API must expose before a sequence can sit on it
Sending a message is the easy part. A sequence also needs a start signal, a delay, a way to cancel, protection against double sends, trustworthy feedback and unsubscribe handling, and vendors differ sharply on each.
A start signal
Event APIs accept a name plus a payload and map it to a trigger: Resend's Send Event, the Loops events/send endpoint and Customer.io's customer events all work this way. A sending API has nothing to trigger, so your own enrolment table plays that role.
Delays and the scheduling horizon
A day-7 reminder cannot be queued at signup if the vendor's window is shorter. SendGrid caps send_at at 72 hours, SparkPost caps start_time at 3 days and Mailgun's o:deliverytime reaches 3 or 7 days by plan, so anything longer needs a scheduler of your own.
A way to cancel what is queued
When a trial user upgrades, the remaining nurture emails must stop. SendGrid cancels or pauses a scheduled batch by batch_id, Brevo lists delete calls by batchId or messageId, Resend has a Cancel Email endpoint, and the event-API tools use exit criteria or goals inside the workflow.
Protection against double sends
A timeout leaves you unsure whether the email left. Resend (24 hours, 256 characters), Loops (24 hours, 100 characters, 409 on reuse) and Customer.io events (a ULID id) document de-duplication; Postmark's send endpoint documents no equivalent, so there you record each step's state before and after the call.
Feedback you can trust
A bounce or complaint has to remove the contact from every later step, so check how webhook calls are authenticated. Resend signs through Svix, SendGrid signs with ECDSA, and Postmark does not sign at all, relying on Basic auth plus an IP allowlist.
Unsubscribe and suppression plumbing
Sequence steps are marketing-class mail and need a working opt-out. Resend's automation docs name an unsubscribe URL variable for non-transactional messages, and Mailjet's exclusion flag blocks campaigns while still allowing transactional sends; on a bare API you store the preference and check it before every step.
What each API offers for sequences, side by side
| Service | Role | Sequences | Official SDKs | Webhook trust | Retry safety | Entry price |
|---|---|---|---|---|---|---|
| #1 Resend | Sending API plus event-triggered automations | Yes: Automations started by a Send Event call | Node.js, PHP, Laravel, Python, Ruby, Go, Java, Rust, .NET | Signed through Svix; events can be replayed | Idempotency-Key header, kept 24 hours | Pro $20/mo at 50k emails |
| #2 Loops | Event API into a hosted sequence builder | Yes: POST /v1/events/send triggers workflows | Official SDKs plus community libraries (Elixir, Go, Java, Laravel, .NET, PHP, Python, Rails) | Signed with a secret; one endpoint per account; retried for about 28 hours | Idempotency-Key header, 24 hours, 409 on reuse | Free to 1,000 contacts; $99/mo at 10k |
| #3 Customer.io | Event API into a branching journey builder | Yes: events trigger Automations with goals and exit criteria | Official libraries on the Track API (v1), plus the newer Pipelines API | Reporting webhooks send delivery and engagement data out | Event id (a ULID) is de-duplicated | Essentials from $100/mo; $145 at 10k profiles |
| #4 Postmark | Sending API with templates and message streams | No: no automation or sequence resource in the API | TypeScript/Node.js, Python, Ruby and Rails, .NET, Java, PHP | Not signed: Basic auth in the URL plus IP allowlist | No idempotency header on /email; dedupe webhook events yourself | Basic $15/mo at 10k emails |
| #5 SendGrid | Mail Send API plus dashboard-built Automation series | Partly: Automation series entered via a list or segment | C#, Go, Java, Node.js, PHP, Python, Ruby | Event Webhook: ECDSA signature and/or OAuth 2.0 | batch_id lets you cancel or pause scheduled sends | Essentials from $19.95/mo |
| #6 Mailgun | Sending API with per-message scheduling | No: o:deliverytime queues single messages | Go, Node.js, PHP, Java, Ruby, Python | Signed: HMAC-SHA256 of timestamp + token, one account-wide key | No idempotency option in the messages reference | Basic $15/mo at 10k emails |
| #7 Amazon SES | Pay-per-message sending primitive | No: no sequence concept at all | The AWS SDK for each language you use | No direct webhook: events go to CloudWatch, Firehose, SNS or EventBridge | None in SendEmail; track sent steps yourself | $0.16 per 1,000 emails (Essentials) |
| #8 SparkPost | Bulk-oriented sending API (Transmissions) | No: start_time schedules a single transmission | Client libraries are listed in its developer docs | Separate webhooks API; read its docs | None documented on the transmissions endpoint | Startup from $15/mo (50K tier) |
| #9 Mailjet | Send API plus dashboard automations in one account | Partly: automations are built in the dashboard | PHP, Node.js, Ruby, Python, Java, Go, C# | Event webhooks plus a Parse API for inbound mail | None documented on /v3.1/send | Essential $19/mo at 15k emails |
| #10 Mandrill | Mailchimp Transactional: blocks of sends | No: sequencing belongs to your code or to Mailchimp marketing | Node.js, PHP, Ruby, Python | Webhooks exist; see the Transactional docs | None documented in the quick start | $20 per 25,000-email block |
| #11 Elastic Email | Email API and a separate marketing product with automations | Partly: one automation on Starter, advanced automation on Pro | REST API v4, a legacy web API and an API libraries page | See its REST API docs | None documented here | Marketing Starter $29/mo (2,500 contacts) |
| #12 SMTP2GO | SMTP relay and JSON send API on flat tiers | No: a single send endpoint with template support | Plain HTTPS JSON; SMTP relay as the alternative | See its developer docs | Returns 200 OK even on errors; read failed and failures | Starter $10/mo at 10k; Professional $75/mo at 100k |
| #13 MailerSend | Sending API with templates and bulk emailing | No: send, templates and bulk emailing only | See its developer docs | See its developer docs | None documented in the email reference | Free at 500 emails/mo; Starter $31.50/mo (billed yearly) |
| #14 Brevo | Transactional API in a marketing, SMS and CRM suite | Partly: automations are built in the dashboard | TypeScript client (@getbrevo/brevo) and plain HTTPS | See its developer docs | scheduledAt plus delete-by-batchId or messageId | Free at 300 sends/day; Standard from $18/mo |
Two ways to build a sequence: who owns the machine
Pure sending API (you own it)
- + Copy, timing and per-person state live in your own database and repo
- + Lowest cost per message: $0.16 per 1,000 on Amazon SES Essentials
- + Changing vendor means swapping one send call, not rebuilding journeys
- - You build and run the scheduler, step tracking and exit conditions
- - Vendor scheduling stops at 72 hours to 7 days, so longer gaps sit in your queue
Event API (the vendor owns it)
- + Starting a journey is one POST with an event name and a payload
- + Delays, branches and exits live in a workflow with per-run logs
- + Non-engineers can edit copy and timing without a deploy
- - Priced by contact: $80 at 10k on Resend Marketing, $99 on Loops, $145 on Customer.io
- - Journey logic lives in the vendor, so leaving means rebuilding it
Where the line falls: if the sequence is a handful of fixed emails already modelled in your database, a pure sender is enough and cheaper. If people outside engineering will keep changing the steps, or you need branches and goals, pay for an event API. Resend sits between the two because the same account offers both a bare send call and Automations.
What each vendor's API actually does
Fourteen services in a fixed order: the ones whose API can run a sequence come first, then the pure senders, then the dashboard-first suites. Each card lists the documentation pages the facts came from.
Resend
Sending API plus event-triggered automationsOne API key covers transactional sends, contacts, events and automations
Free is 3,000 emails/mo (100 a day). Pro reads $20 at 50,000 and $35 at 100,000 on its slider; the Marketing tab is free up to 1,000 contacts.
Resend's API reference is split into resource groups: Sending, Receiving, Broadcasts, Automations, Events, Templates, Contacts, Segments, Topics, Suppressions, Webhooks, Logs and Domains. For sequence work the useful trio is Events, Automations and Templates. You define an event name (optionally with a payload schema), call Send Event with either a contact ID or an email address plus a payload, and every enabled automation whose trigger step listens for that name starts a run. If no contact exists for the email, one is created when the automation runs.
Inside an automation, Resend's documentation describes a trigger, delay steps, wait-for-event steps, conditions that branch the flow, and send-email steps that render a template with variables. Each run records the status of every step, so a stalled onboarding journey can be inspected without digging through your own logs. The API also lists Create, Update, Duplicate, Stop and Delete Automation calls plus List and Retrieve Automation Run, which means flows can be managed from code instead of only from the dashboard. Automation emails that are not transactional are expected to include the unsubscribe URL variable the docs name.
Retry safety is a request option rather than a made-up header: the Idempotency-Key HTTP header (the SDKs expose it as idempotencyKey) accepts up to 256 characters, is remembered for 24 hours and works on Send Email and Send Batch Emails. The docs suggest keys shaped like welcome-user/123456789. Webhooks are signed through Svix; the verification page tells you to hand the raw body, the headers and the endpoint's signing secret to the SDK or the Svix libraries instead of writing your own signature check. Event types include email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.opened, email.clicked, email.failed and email.suppressed, plus contact, domain, topic and suppression events. Official SDKs are published for Node.js, PHP, Laravel, Python, Ruby, Go, Java, Rust and .NET.
Pricing on resend.com/pricing has a Transactional tab and a Marketing tab with separate sliders, so read the one your automation emails count against before you budget. Transactional: Free is 3,000 emails a month, Pro is $20 for 50,000 (the slider reads $35 at 100,000) and Scale is $90 for 100,000 ($160 at 200,000). Marketing, priced by contacts: free at 1,000 contacts with 10,000 automation runs and 3 segments, then $40 at 5,000 contacts, $80 at 10,000, $250 at 50,000 and $450 at 100,000, per slider readouts dated 7 October 2026.
Yes: Automations started by a Send Event call
Node.js, PHP, Laravel, Python, Ruby, Go, Java, Rust, .NET
Signed through Svix; events can be replayed
Idempotency-Key header, kept 24 hours
Sources: pricing page , Send Email reference, Idempotency keys, Automations overview, Send Event reference, Verifying webhooks
Loops
Event API into a hosted sequence builderYour code reports what happened; Loops workflows decide what to send
Free caps at 4,000 sends per rolling 30 days to the 1,000 newest contacts. Paid scales by subscribers: $99/mo at 10k and $249/mo at 50k per its dated slider readout.
Loops sits at the opposite end from a raw sender. The Send Event endpoint takes an email and/or a userId, an eventName, optional eventProperties and mailing-list memberships, and the documented example also passes contact fields such as planName in the same call. Workflows built in the Loops editor listen for those event names, so the application only ever posts a short JSON body with a Bearer token. The API reference groups the rest of the surface into contacts, events, event patterns, mailing lists, themes, transactional emails and transactional groups, uploads, workflows and workflow nodes, and Loops also publishes a CLI and an MCP server.
Duplicate protection is documented: send an Idempotency-Key header of up to 100 characters, and a key that was already used in the previous 24 hours returns 409 Conflict instead of triggering a second send. The SDK page separates libraries that Loops maintains from community ones, and it states that it has not reviewed the Elixir, Go, Java, Laravel, .NET, PHP, Python and Rails packages, so check which bucket your language falls into. Transactional email is also available over SMTP for popular frameworks.
Loops webhooks are signed: the settings page gives you a signing secret, and its documentation shows a verification function for Next.js. You can register only one endpoint per account, events are sent at up to 10 a second, and a non-2xx answer triggers up to eight attempts over about 28 hours. The same event can arrive more than once, so Loops tells you to return a 2xx immediately and de-duplicate on the Webhook-Id header. If every attempt fails for 5 days the endpoint is disabled until you switch it back on.
Pricing is by contact, not by email. Per loops.so/pricing, the free plan allows up to 4,000 sends in any rolling 30 days to your 1,000 newest contacts, carries a small Powered by Loops footer and sends up to 10 emails a second. Paid plans send without a per-email charge, include transactional sends, and scale with subscribers: $99/mo at 10k and $249/mo at 50k on the slider readout dated 7 October 2026.
Yes: POST /v1/events/send triggers workflows
Official SDKs plus community libraries (Elixir, Go, Java, Laravel, .NET, PHP, Python, Rails)
Signed with a secret; one endpoint per account; retried for about 28 hours
Idempotency-Key header, 24 hours, 409 on reuse
Sources: pricing page , Send event reference, Webhooks, SDKs and SMTP
Customer.io
Event API into a branching journey builderThe Track API feeds events and attributes; Automations run multi-step journeys
Essentials selector: $145 at 10k profiles, $235 at 20k, $505+ at 50k+. Premium starts at $1,000/mo billed yearly.
Customer.io is the heavyweight in this group. Events and profile attributes go in through the Track API, which uses HTTP Basic auth with a site ID and secret key; the US base URL is track.customer.io and EU workspaces use track-eu.customer.io. PUT /api/v1/customers/{identifier} creates or updates a person, and POST /api/v1/customers/{identifier}/events records an event with a name and a data object that message templates can reference. An anonymous-event endpoint exists for visitors you have not identified yet, and the docs steer new integrations that need objects or batching toward the Pipelines API.
An automation has a trigger (segment change, event, form submission and more), optional goals, exit criteria and the workflow itself. People move through it as a journey until they hit the goal or an exit condition, which is precisely the piece that a bare sending API forces you to rebuild. Events can carry an id, a ULID that Customer.io uses to ignore repeats, though its OpenAPI description notes the Python and Ruby libraries do not pass it. The documented rate guideline for the Track API is 1,000 requests per second.
Per customer.io/pricing, Essentials starts at $100 a month, includes 1M monthly email sends and reads $145 at 10k profiles and $505+ at 50k+ profiles on the selector, billed monthly. Premium starts at $1,000 a month billed yearly. The page also lists US and EU data centers, a published DPA, and a startup program for companies that have raised under $10M (12 months free).
Yes: events trigger Automations with goals and exit criteria
Official libraries on the Track API (v1), plus the newer Pipelines API
Reporting webhooks send delivery and engagement data out
Event id (a ULID) is de-duplicated
Sources: pricing page , Track API reference, Automation concepts
Postmark
Sending API with templates and message streamsA small, strict send API; scheduling and sequence state stay in your code
Free is 100 emails/mo with no overages. Slider stops: 10k, 50k, 125k and 300k emails; Basic reads $15, $55 and $115 at the first three.
Postmark's API is deliberately small. POST /email sends one message (up to 50 recipients across To, Cc and Bcc) and POST /email/batch takes up to 500 messages and 50 MB per call. Every call carries an X-Postmark-Server-Token header, From must be a confirmed Sender Signature, and a MessageStream field picks the stream; leave it out and the message goes to the default outbound transactional stream. Tag and Metadata fields let you label each send, which is how you would record "step 3 of the trial series" without a database lookup.
For sequences, the important fact is what is missing: the documentation covers sending, templates, message streams, bounces, stats and webhooks, and includes no automation or sequence resource. The path its TypeScript quick start recommends is sendEmailWithTemplate with a TemplateAlias and a TemplateModel, which keeps the copy and layout in Postmark while the timing logic lives in your scheduler. Official libraries are listed for TypeScript and Node.js, Python, Ruby and Rails, .NET, Java and PHP.
Webhooks are not signed. The docs say HMAC signature verification is not supported and recommend HTTP Basic authentication embedded in the webhook URL, IP allowlisting, and shape checks inside your handler. When you save a webhook, Postmark calls your endpoint once per enabled event type and expects a 200; if an event type keeps failing, it flags that type as failing its check and pauses delivery for it, so a broken Bounce handler stops bounce events specifically instead of silently dropping everything. Delivery and Bounce events for one message share its MessageID, so dedupe on RecordType plus MessageID rather than MessageID alone.
Per postmarkapp.com/pricing, the slider stops at 10,000, 50,000, 125,000 and 300,000 emails. At 10,000 it reads $15 on Basic, $16.50 on Pro and $18 on Platform; at 50,000, $55, $60.50 and $66; at 125,000, $115, $126.50 and $138. There is no 100,000 stop to quote.
No: no automation or sequence resource in the API
TypeScript/Node.js, Python, Ruby and Rails, .NET, Java, PHP
Not signed: Basic auth in the URL plus IP allowlist
No idempotency header on /email; dedupe webhook events yourself
Sources: pricing page , Email API reference, Webhooks overview, Official libraries
SendGrid
Mail Send API plus dashboard-built Automation seriesTwo halves: a send API with scheduling, and list-driven drip series in Marketing Campaigns
Free trial: 100 emails/day for 60 days. Essentials starts at $19.95 (selector spans 50k to 100k), Pro at $89.95; Marketing Campaigns is priced by contacts, from free to $15 and $60.
SendGrid's API surface splits in two. Mail Send (v3) takes a personalizations array in which each object acts as its own envelope, carrying recipients, substitutions or dynamic template data, and custom_args. A template_id beginning with d- identifies a Dynamic Template written in Handlebars. Scheduling is built in: send_at accepts a Unix timestamp but never more than 72 hours ahead, and a batch_id lets you cancel or pause the scheduled batch later.
Drip series live in the other half, Marketing Campaigns. Its Automation feature builds a Welcome or Custom series: contacts enter when they are added to the linked list or segment, leave when the exit criteria are met (for example after receiving every email in the series), and an unsubscribe group, a sender and a contact list have to exist first. For a developer that makes the integration point getting people into the right list; the delays and email order are configured in the console, not sent per call.
Official libraries cover C#, Go, Java, Node.js, PHP, Python and Ruby. The Event Webhook offers two independent security methods that can run together: an ECDSA signature delivered in the X-Twilio-Email-Event-Webhook-Signature header and checked with the public key SendGrid shows you, and OAuth 2.0. Twilio's warning is worth repeating here: verify against the raw bytes of the payload and parse the JSON afterwards, because re-serialising can alter characters that were part of the signature.
Twilio's Email API pricing page lists a free trial of 100 emails a day for 60 days, Essentials from $19.95 a month (the selector spans 50,000 to 100,000 emails) and Pro from $89.95 a month (100,000 to 2,500,000, dedicated IPs included), with overages and taxes extra. Marketing Campaigns is priced separately by contacts: Free covers up to 2,000 contacts and 6,000 emails a month, Basic starts at $15 and Advanced at $60.
Partly: Automation series entered via a list or segment
C#, Go, Java, Node.js, PHP, Python, Ruby
Event Webhook: ECDSA signature and/or OAuth 2.0
batch_id lets you cancel or pause scheduled sends
Sources: pricing page , Mail Send reference, Event Webhook security, Getting started with Automation, Official libraries
Mailgun
Sending API with per-message schedulingForm-field options on one send call, with a scheduling window that depends on plan
Free is 100 emails/day. Basic $15 at 10,000, Foundation $35 at 50,000, Scale $90 at 100,000 per its pricing page.
Mailgun's send call is POST /v3/{domain}/messages with HTTP Basic auth. You supply text, html, amp-html or a stored template, and Mailgun-specific behaviour rides on prefixed form fields: o: for options, v: for custom variables and h: for headers. A recipient-variables field takes a JSON map of per-recipient values so one call can fan out personalised copies, and the send options as a group are limited to 16 KB.
Scheduling is the feature that matters for sequences. o:deliverytime takes an RFC 2822 date and, depending on plan, can sit 3 or 7 days in the future, so a day-2 or day-3 step can be queued at signup but a day-14 step cannot. A separate o:deliverytime-optimize-period option switches on send-time optimisation with a window of 24 to 72 hours. Nothing in the messages reference models a series, so anything longer than the window needs a timer of your own.
Webhooks are configured at account or domain level and can subscribe to accepted, delivered, temporary_fail, permanent_fail, opened, clicked, unsubscribed and complained events. Each POST carries a token, a timestamp and a signature, which is a hex-encoded HMAC-SHA256 of the timestamp and token keyed with your account-wide Webhook Signing Key (a separate secret from your API key and your sending key). Mailgun's pricing page ties log retention to the plan: one day on Free and Basic, five on Foundation and 30 on Scale, which decides how far back you can look when a customer says the day-3 email never arrived. Official SDKs are listed for Go, Node.js, PHP, Java, Ruby and Python.
Per mailgun.com/pricing, Free is 100 emails a day, Basic is $15 a month for 10,000 emails, Foundation is $35 a month for 50,000 (after the first month) and Scale is $90 a month for 100,000 (after the first month). Extra emails start at $1.80 per 1,000 on Basic and $1.30 on Foundation. US and EU hosting regions are offered.
No: o:deliverytime queues single messages
Go, Node.js, PHP, Java, Ruby, Python
Signed: HMAC-SHA256 of timestamp + token, one account-wide key
No idempotency option in the messages reference
Sources: pricing page , Messages reference, Webhooks, Securing webhooks, SDK index
Amazon SES
Pay-per-message sending primitiveThe cheapest last step in the chain, with every other step left to you
Essentials $0.16 per 1,000 for 0 to 10M a month. Pro $0.22 per 1,000 plus $105 per account per region per month; Enterprise $0.23 plus $500.
Amazon SES v2 sends through POST /v2/email/outbound-emails, which the SDKs expose as SendEmail. The request carries a Content block that is Simple (subject plus HTML and text bodies), Raw (a complete MIME message) or Template (a stored template filled from TemplateData), a Destination with To, Cc and Bcc lists, EmailTags, and an optional ConfigurationSetName. A ListManagementOptions field ties a send to a contact list and topic so that unsubscribes are honoured.
SES has no sequence concept at all. What it gives you is the inexpensive, high-capacity final step plus event publishing: a configuration set routes sends, deliveries, opens, clicks, bounces, complaints, rejections, rendering failures and delivery delays to CloudWatch, Data Firehose, Pinpoint, SNS or EventBridge. Delay logic, per-person state and exit conditions are yours to build with whichever queue or scheduler you already operate, and reaching your own HTTP endpoint means going through one of those AWS services.
Authentication is AWS's own: IAM credentials and request signing, handled by the AWS SDK for your language. In JavaScript the v3 package is @aws-sdk/client-sesv2, with SESv2Client and SendEmailCommand. Per aws.amazon.com/ses/pricing, the Essentials plan is $0.16 per 1,000 emails for the first 10 million a month, Pro is $0.22 per 1,000 plus $105 per account per region per month, and Enterprise is $0.23 per 1,000 plus $500. The page says new accounts start on Essentials from July 21, 2026 unless another plan is chosen, and eligible new AWS customers can receive up to $200 in credits.
No: no sequence concept at all
The AWS SDK for each language you use
No direct webhook: events go to CloudWatch, Firehose, SNS or EventBridge
None in SendEmail; track sent steps yourself
Sources: pricing page , SendEmail (API v2), Event publishing, JavaScript v3 SendEmailCommand
SparkPost
Bulk-oriented sending API (Transmissions)One call can address thousands of recipients, each with their own substitution data
Free is 1,000 emails/mo with a 50-a-day cap. Startup from $15 for 50K to 100K, Growth from $80 for 100K to 2.5M, Enterprise custom.
SparkPost's send endpoint is the Transmissions API, POST /api/v1/transmissions. One transmission can target a single address or thousands, listed inline or pulled from a stored recipient list, with substitution data and metadata set per recipient, and content that is either inline or a template_id pointing at a stored template. Options in the same body control engagement tracking, sending optimisation and global substitution data.
For delayed steps, putting start_time inside options schedules the transmission, with a limit of 3 days ahead. As with every sender in this group that lacks a sequence object, a multi-week series means your scheduler creates each transmission when its step comes due. The recipient-list model suits broadcast-style steps (one message to a whole cohort) better than per-user event triggers, where you would create a one-recipient transmission per person.
SparkPost's pricing page lists Free at 1,000 emails a month with a cap of 50 a day, Startup from $15 a month for 50K to 100K emails, Growth from $80 a month for 100K to 2.5M, and custom Enterprise rates beyond that. The 50-a-day cap means a launch-day signup spike stalls well before the monthly total is reached.
No: start_time schedules a single transmission
Client libraries are listed in its developer docs
Separate webhooks API; read its docs
None documented on the transmissions endpoint
Sources: pricing page , Transmissions API
Mailjet
Send API plus dashboard automations in one accountKeep transactional calls in code and let marketers own the lifecycle copy
Free is 6,000 emails/mo (200 a day). Starter $9 for 8,000 emails; Essential $19 and Premium $29 for 15,000.
Mailjet's Send API v3.1 is POST /v3.1/send, authenticated with HTTP Basic auth built from an API key and a secret key. To send a stored template you pass TemplateID with TemplateLanguage set to true, and the Content API (v1) manages templates and their contents programmatically. Official wrappers exist for PHP, Node.js, Ruby, Python, Java, Go and C#, and the Node.js one documents batch sending and TypeScript types.
Mailjet runs sending and marketing in one product (contact lists, segmentation, an editor, automations and SMS), so a team can keep transactional calls in code and let marketers edit lifecycle emails in the dashboard. Its exclusion-list documentation draws a distinction that matters for sequences: a contact flagged IsExcludedFromCampaigns is blocked from marketing campaigns but still receives transactional messages sent through the Send API. A Parse API handles inbound mail by pointing a parse route at your webhook URL.
On mailjet.com/pricing, Free is 6,000 emails a month with a 200-a-day limit and 1,000 contacts; Starter is $9 a month for 8,000 emails and 2,000 contacts; Essential is $19 a month for 15,000 emails with unlimited contacts; Premium is $29 a month for 15,000 emails without the daily limit. The tiers quoted here stop at 15,000 emails a month; larger volumes go through the plan selector on the page.
Partly: automations are built in the dashboard
PHP, Node.js, Ruby, Python, Java, Go, C#
Event webhooks plus a Parse API for inbound mail
None documented on /v3.1/send
Sources: pricing page , Email API overview, SDKs and client libraries, Exclusion list, Send API with stored templates
Mandrill
Mailchimp Transactional: blocks of sendsA transactional API sold in 25,000-email blocks, with a rules engine and subaccounts
$20 per block for 1 to 20 blocks a month (up to 500,000 emails); unused emails do not roll over. New users can get 500 free sends to a verified domain.
Mandrill is Mailchimp Transactional: a transactional API and SMTP service you can open as its own account or enable from a Mailchimp Standard plan or higher. Requests authenticate with an API key that carries full account access, so keep it out of anything client-side. The quick start lists client libraries for Node.js (@mailchimp/mailchimp_transactional), PHP (mailchimp/transactional), Ruby (MailchimpTransactional) and Python (mailchimp_transactional), and says you can also send over SMTP.
Two features stand out for lifecycle senders. A rules engine changes sending behaviour when searchable criteria match, and subaccounts isolate different products or clients inside one login. The API itself sends messages; the timing of a multi-email series belongs to your own scheduler or to Mailchimp's marketing product.
Mandrill's pricing page sells blocks of 25,000 emails: $20 per block for 1 to 20 blocks a month (up to 500,000 emails), falling to $18 per block for 21 to 40 blocks, with unused emails not rolling over. That commitment model fits steady volume better than a spiky launch, because a month with fewer sends still costs the whole block.
No: sequencing belongs to your code or to Mailchimp marketing
Node.js, PHP, Ruby, Python
Webhooks exist; see the Transactional docs
None documented in the quick start
Sources: pricing page , Transactional API quick start, Mandrill pricing
Elastic Email
Email API and a separate marketing product with automationsThe API product and the contact-priced marketing plans are bought separately
Free covers up to 1,000 contacts and 3,000 emails/mo (100 a day). Starter $29 and Pro $49 both cover 2,500 contacts and 37,500 emails.
Elastic Email sells an Email API (SMTP plus HTTP) and an Email Marketing product separately, and its developer menu lists REST API docs for the v4 API, a legacy web API and a page of API libraries. For sequence work the marketing plans are the relevant ones, because automation sits there: Starter includes a single automation, and Pro adds advanced automation and user management, both within 2,500 contacts and 37,500 emails a month.
Pricing on elasticemail.com/pricing: Free covers up to 1,000 contacts and 3,000 emails a month with a 100-a-day limit, Starter is $29 a month and Pro is $49 a month. The Email API product has its own pricing page, and that is the one to read if all you want is a sender.
Partly: one automation on Starter, advanced automation on Pro
REST API v4, a legacy web API and an API libraries page
See its REST API docs
None documented here
Sources: pricing page , REST API v4 documentation
SMTP2GO
SMTP relay and JSON send API on flat tiersA plain send endpoint that is easy to adopt when your app already speaks SMTP
Free is up to 1,000 emails/mo (200 a day). Extra emails cost $1 per 1,000 on Starter and $0.85 per 1,000 on Professional.
SMTP2GO offers an SMTP relay and an HTTP API. POST https://api.smtp2go.com/v3/email/send takes a JSON body with sender, a to array of up to 100 recipients, subject, html_body or text_body, custom_headers, attachments and inlines, or a template_id with template_data (in which case subject and bodies are ignored). The reference recommends setting fastaccept to true, which it says is a much faster method and will become the default.
One quirk the reference spells out matters for any sequence worker: the endpoint returns 200 OK even when processing errors occurred, so your code has to inspect the failed and failures properties in the response. A worker that checks only the HTTP status would mark failed steps as sent and the contact would silently skip an email.
On smtp2go.com/pricing, Free is up to 1,000 emails a month (200 a day), Starter is $10 a month or $100 a year for 10,000 emails with extra emails at $1 per 1,000, and Professional is $75 a month or $750 a year for 100,000 emails with extra at $0.85 per 1,000. Those flat steps make it the easiest bill to predict among the pure senders here.
No: a single send endpoint with template support
Plain HTTPS JSON; SMTP relay as the alternative
See its developer docs
Returns 200 OK even on errors; read failed and failures
Sources: pricing page , Send a standard email
MailerSend
Sending API with templates and bulk emailingA sender with templates; a sequence is still your scheduler
Free covers 500 emails/mo and 100 API requests a day. Hobby is $75.60 billed yearly for 5,000 emails; Starter is $378 billed yearly for 50,000.
MailerSend sends through POST https://api.mailersend.com/v1/email. The core fields are from, to (1 to 50 recipients), subject, and one of text, html or template_id; when a template carries a default sender and subject, those two fields become optional. Text and HTML bodies are capped at 2 MB each, and attachments at 25 MB after Base64 decoding.
Per mailersend.com/pricing, Free covers 500 emails a month through the Email API and SMTP relay, with 100 daily API requests, one domain and one user. Hobby is $75.60 billed yearly for 5,000 emails, Starter is $378 billed yearly for 50,000 emails, and Professional is $1,188 billed yearly for 50,000 emails plus 150 SMS a month. The 100-a-day API request limit on Free makes it a prototype tier rather than a place to run a drip series.
The documented features are sending, templates, bulk emailing, verification, analytics and an SMS API. Sequencing is not among them, so the delay and exit logic of a lifecycle series stays with your own worker.
No: send, templates and bulk emailing only
See its developer docs
See its developer docs
None documented in the email reference
Sources: pricing page , Email endpoint reference
Brevo
Transactional API in a marketing, SMS and CRM suiteSend API with scheduling and cancellation, automations built in the dashboard
Free allows 300 sends a day and 2,000 contacts in marketing automation. Starter is $9, Standard $18 (automation, A/B testing), Professional $499.
Brevo's transactional endpoint is POST https://api.brevo.com/v3/smtp/email. You send either inline htmlContent or a templateId; scheduledAt queues the send for later (UTC), messageVersions sends several personalised versions in one call (2,000 recipients in total and 99 per version), and the reference lists calls to fetch or delete scheduled emails by batchId or messageId, which is how a queued reminder gets cancelled when the user converts. The TypeScript client is BrevoClient from @getbrevo/brevo.
Marketing automation lives in the same account. The Free plan allows 2,000 contacts in marketing automation and 300 sends a day; Starter is $9 a month with no daily cap; Standard, from $18 a month and starting at 5,000 emails, is the first paid plan listed with automation and A/B testing; Professional starts at $499 a month for 150,000 to 10 million sends, per brevo.com/pricing. The split is similar to SendGrid's: code sends and schedules, the dashboard owns the drip logic.
Partly: automations are built in the dashboard
TypeScript client (@getbrevo/brevo) and plain HTTPS
See its developer docs
scheduledAt plus delete-by-batchId or messageId
Sources: pricing page , Send a transactional email
Worked costs at 10,000, 50,000 and 100,000 emails a month
A four-email onboarding series sent to 2,500 new signups a month is 10,000 emails; to 12,500 signups it is 50,000. These are list prices read from each vendor's pricing page on 7 October 2026, before the engineering time that a pure sender needs. A dash means the page's recorded tiers do not give a figure at that volume.
| Sender | 10,000 emails | 50,000 emails | 100,000 emails | Arithmetic and source |
|---|---|---|---|---|
| Resend | $20 | $20 | $35 | Pro covers 50,000 for $20 and its slider reads $35 at 100,000; Free (3,000) is too small for 10,000. resend.com/pricing |
| Postmark (Basic) | $15 | $55 | - | Slider stops are 10k, 50k, 125k and 300k; Basic reads $115 at 125,000. postmarkapp.com/pricing |
| Mailgun | $15 | $35 | $90 | Basic 10,000, Foundation 50,000, Scale 100,000; Foundation and Scale prices apply after the first month. mailgun.com/pricing |
| Amazon SES (Essentials) | $1.60 | $8.00 | $16.00 | 10 x $0.16, 50 x $0.16 and 100 x $0.16 per 1,000 emails. aws.amazon.com/ses/pricing |
| SMTP2GO | $10 | $50 | $75 | Starter $10 includes 10,000; at 50,000 that is $10 + 40 x $1 for the extra 40,000; Professional is $75 at 100,000. smtp2go.com/pricing |
| Mandrill | $20 | $40 | $80 | 1, 2 and 4 blocks of 25,000 at $20 per block (the 1 to 20 block band). mandrill.com/pricing |
| SendGrid | - | from $19.95 | - | Essentials starts at $19.95 and its selector begins at 50,000 emails; Pro starts at $89.95. Twilio pricing |
| SparkPost | - | from $15 | - | Startup covers the 50K to 100K tier from $15; Free is 1,000 a month. sparkpost.com/pricing |
| Mailjet | $19 | - | - | Essential gives 15,000 emails for $19; recorded tiers stop there. mailjet.com/pricing |
Contact-priced sequence tools at 10,000, 50,000 and 100,000 contacts
Event-API tools charge by people rather than by message, so the comparison changes. Slider readouts dated 7 October 2026.
| Tool | 10,000 contacts | 50,000 contacts | 100,000 contacts | What is included |
|---|---|---|---|---|
| Resend Marketing | $80 | $250 | $450 | Unlimited broadcast sending and segments on paid sliders; Free is 1,000 contacts with 10,000 automation runs. resend.com/pricing |
| Loops | $99 | $249 | - | Unlimited sends to subscribers and transactional sends on paid plans. loops.so/pricing |
| Customer.io Essentials | $145 | $505+ | - | 1M monthly email sends included; additional profiles are listed at $0.009 each. customer.io/pricing |
Reading the two tables together: a four-email series to 10,000 people is 40,000 sends. On Amazon SES Essentials that is 40 units of 1,000 at $0.16 each; on SMTP2GO it is the $10 Starter plan plus 30 units of 1,000 extra at $1 each. The same 10,000 contacts on an event-API tool read $80 on Resend Marketing, $99 on Loops and $145 on Customer.io. The gap is the price of having someone else run the scheduler, branching and exit logic, and it narrows quickly if you value an engineer's week at more than the monthly difference.
How each vendor reports delivery events and proves they are real
A sequence that ignores bounces keeps mailing dead addresses, and one that trusts forged webhooks can be talked into unsubscribing real customers. The six services below document their event delivery in enough detail to compare.
| Service | How an event reaches you | How you check it is genuine | Events named in its docs |
|---|---|---|---|
| Resend | HTTPS POST to an endpoint you register in the dashboard or API; failed events can be replayed | Svix signing secret per endpoint, verified with the SDK or Svix libraries over the raw body and headers | email.sent, delivered, delivery_delayed, bounced, complained, opened, clicked, failed, suppressed, plus contact, domain, topic and suppression events |
| Postmark | HTTPS POST per message stream; saving the webhook triggers a test call per event type | No signature. Basic auth credentials in the URL plus an IP allowlist, and your own payload checks | Delivery, bounce, spam complaint, open, click and subscription change |
| SendGrid | Event Webhook posts mail events to your URL | ECDSA signature in the X-Twilio-Email-Event-Webhook-Signature header, OAuth 2.0, or both | You choose which actions to post when you create the webhook |
| Loops | HTTPS POST to the single endpoint allowed per account; up to eight attempts over about 28 hours | Signing secret from the settings page; de-duplicate repeats on the Webhook-Id header | Grouped event toggles in Settings, Webhooks |
| Mailgun | HTTP or HTTPS POST with a JSON payload, set at account or domain level | HMAC-SHA256 of timestamp + token, keyed with an account-wide Webhook Signing Key | accepted, delivered, temporary_fail, permanent_fail, opened, clicked, unsubscribed, complained |
| Amazon SES | No direct webhook: a configuration set publishes to CloudWatch, Data Firehose, Pinpoint, SNS or EventBridge | Events land in an AWS service in your own account, so there is no inbound request from SES to authenticate | Sends, deliveries, opens, clicks, bounces, complaints, rejections, rendering failures, delivery delays |
Whatever the vendor, acknowledge quickly and process later: write the raw event to a queue or table, return a 2xx, and let a worker update suppression state. Postmark's own guide uses the same pattern, returning non-2xx only when processing fails so that its retry fires, and it checks each event against stored IDs first so a retry does not act twice.
Seven working patterns, each checked against current docs
Every sample follows the documentation page linked under it, in Node.js or TypeScript. Product names, addresses and IDs are stand-ins you replace; the endpoints, field names and headers are the vendors' own.
Resend: a retry-safe send, then an event that starts an automation
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
// One-off message. Repeating this call with the same key within 24 hours does not send twice.
await resend.emails.send(
{
from: 'Acme <onboarding@resend.dev>',
to: ['delivered@resend.dev'],
subject: 'hello world',
html: '<p>it works!</p>',
},
{ idempotencyKey: 'welcome-user/123456789' },
);
// Start every enabled automation whose trigger step listens for "user.created".
await resend.events.send({
event: 'user.created',
email: 'delivered@resend.dev',
payload: { plan: 'pro' },
}); Checked against Resend's idempotency keys and Send Event pages. The event name must match one configured in an automation trigger step; if no contact exists for the email, one is created when the automation runs.
Loops: report an event and let a workflow take over
// POST https://app.loops.so/api/v1/events/send
const res = await fetch('https://app.loops.so/api/v1/events/send', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.LOOPS_API_KEY,
'Content-Type': 'application/json',
// Up to 100 characters; a reused key within 24 hours returns 409 Conflict.
'Idempotency-Key': 'signup/usr_7f8e9d0c1b2a',
},
body: JSON.stringify({
email: 'alex@company.com',
userId: 'usr_7f8e9d0c1b2a',
eventName: 'signup',
eventProperties: { plan: 'pro' },
}),
}); Checked against Loops' Send event reference. The workflow that listens for the signup event is built in the Loops editor, not in this call.
Customer.io: record an event for a known person
// Track API: HTTP Basic auth, site ID as user name, secret key as password.
// EU workspaces use track-eu.customer.io instead.
const auth = Buffer.from(process.env.CIO_SITE_ID + ':' + process.env.CIO_API_KEY).toString('base64');
await fetch('https://track.customer.io/api/v1/customers/usr_7f8e9d0c1b2a/events', {
method: 'POST',
headers: { Authorization: 'Basic ' + auth, 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'trial_started',
data: { plan: 'pro' },
}),
}); Checked against the Track API reference and its OpenAPI description of POST /api/v1/customers/{identifier}/events. An automation triggered by the trial_started event then decides what to send.
Postmark: send a stored template into the default stream
import { ServerClient } from 'postmark';
const postmark = new ServerClient(process.env.POSTMARK_SERVER_TOKEN);
// Step 1 of a series. The copy lives in the "welcome" template; your scheduler decides when this runs.
const message = await postmark.sendEmailWithTemplate({
From: 'welcome@acme.example',
To: 'new.user@acme.example',
TemplateAlias: 'welcome',
TemplateModel: { name: 'Alex' },
MessageStream: 'outbound',
});
// Store the ID so webhook events can be tied back to this step.
console.log(message.MessageID); Checked against Postmark's official libraries page, whose TypeScript quick start recommends sendEmailWithTemplate with a TemplateAlias and TemplateModel, and the Email API reference for MessageStream and MessageID.
Amazon SES v2: a tagged send routed through a configuration set
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';
const client = new SESv2Client({ region: 'us-east-1' });
await client.send(new SendEmailCommand({
FromEmailAddress: 'hello@yourapp.dev',
Destination: { ToAddresses: ['new.user@yourapp.dev'] },
Content: {
Simple: {
Subject: { Data: 'Welcome aboard' },
Body: { Html: { Data: '<p>Step 1 of the onboarding series</p>' } },
},
},
// The configuration set decides where bounce, complaint and delivery events are published.
ConfigurationSetName: 'lifecycle-events',
EmailTags: [
{ Name: 'sequence', Value: 'onboarding' },
{ Name: 'step', Value: '1' },
],
})); Checked against the SendEmail (API v2) reference and the JavaScript SDK v3 command page. The older @aws-sdk/client-ses package targets the first SES API and uses different field names.
Webhook receiver: dedupe, store, acknowledge (Postmark payload fields)
import express from 'express';
const app = express();
// Postmark does not sign webhooks. Put Basic auth in the URL you register
// (https://user:pass@yourapp.dev/webhooks/postmark) and allowlist Postmark's IPs.
app.post('/webhooks/postmark', express.json(), async (req, res) => {
const event = req.body;
if (!event.RecordType || !event.MessageID) return res.sendStatus(400);
// Delivery and Bounce share a MessageID, so include the record type in the key.
const key = event.RecordType + ':' + event.MessageID;
if (await db.webhookEvents.exists(key)) return res.sendStatus(200);
await db.webhookEvents.insert(key, event); // a worker applies suppressions later
res.sendStatus(200);
}); Pattern from Postmark's webhooks overview: validate the payload shape, check the ID against stored events before acting, and hand off to durable storage before returning 200. The db helper is your own storage layer.
A sequence worker on a pure sender (Resend as the last hop)
// Runs every minute. sequenceSteps is your own table: person, sequence, step, due time, status.
const due = await db.sequenceSteps.findDue(new Date());
for (const item of due) {
// Exit rules first: unsubscribed, bounced or converted people never get the next step.
if (await db.suppressions.has(item.email) || await db.people.hasConverted(item.personId)) {
await db.sequenceSteps.cancel(item.id);
continue;
}
await db.sequenceSteps.markSending(item.id);
const { error } = await resend.emails.send(
{ from: 'Acme <onboarding@resend.dev>', to: [item.email], subject: item.subject, html: item.html },
// Sequence + step + person: a retry of the same step can never double-send within 24 hours.
{ idempotencyKey: item.sequence + '/' + item.step + '/' + item.personId },
);
if (error) await db.sequenceSteps.retryLater(item.id, error.message);
else await db.sequenceSteps.markSent(item.id);
} The send call and the idempotencyKey option come from Resend's idempotency documentation; the table, the due-step query and the exit rules are application code you own. Resend remembers a key for only 24 hours, so your own status column, not the key, is the long-term guard against duplicates.
Choose A if, choose B if
Six situations that come up when a team decides what its lifecycle email will sit on, each with a first choice and the nearest alternative.
One vendor for password resets and onboarding sequences
Choose Resend if your stack is in one of its nine official SDK languages and you want the send call, contacts, events and Automations behind one key. The Send Event call is the whole integration for a trigger, and the two pricing tabs let you budget transactional volume and marketing contacts separately.
Choose Loops instead if the people editing the sequences are founders or marketers and you prefer one flat per-contact price ($99/mo at 10k) with sends included.
Branching journeys with goals and exit conditions
Choose Customer.io if a journey needs a goal, early exit criteria and several channels, and the $145 it reads at 10k profiles (or $505+ at 50k) is acceptable. Its Track API and Automations were built around that model.
Choose Resend Automations instead if your branches are simple conditions and wait-for-event steps, since $80 at 10,000 contacts undercuts Customer.io for the same list.
You already run queues, workers and a database of users
Choose Postmark if you want a small, strict API with templates and message streams, and you accept that its webhooks are protected by Basic auth and IP allowlisting rather than signatures. The slider reads $15 at 10,000 emails and $55 at 50,000 on Basic.
Choose SMTP2GO instead if a flat $10 at 10k and $75 at 100k suits your budgeting better, and you will remember to read the failed and failures fields because its endpoint answers 200 OK even on errors.
Per-message cost dominates the decision
Choose Amazon SES if sending is already inside AWS and an engineer can own the scheduler, the event routing through a configuration set and the suppression handling. At $0.16 per 1,000 on Essentials, 100,000 emails is $16.
Choose Mailgun instead if you want a hosted API with per-message scheduling up to 3 or 7 days and signed webhooks, at $35 for 50,000 emails on Foundation.
Developers own the triggers, marketers own the copy
Choose SendGrid, Mailjet or Brevo if your code should only send transactional mail and enrol contacts while the drip series is edited in a dashboard. SendGrid's Automation series is entered through a list or segment; Mailjet's free tier (6,000 emails a month) and Brevo's free automation allowance (2,000 contacts) both let you prototype before paying.
Choose Elastic Email instead if a single automation inside the $29 Starter marketing plan covers your series.
Your marketing lists already live in Mailchimp
Choose Mandrill if you want transactional sending under the same Mailchimp login and can use a steady 25,000-email block at $20, remembering that unused emails do not roll over.
Choose SparkPost instead if your sends are large broadcast-style batches, since one transmission can carry thousands of recipients with per-recipient substitution data.
Pick the design first, then the vendor
Decide whether your team will own the scheduler or rent one, then shortlist two vendors from that half of the page and run the cost rows for your own volume. If the shortlist includes Resend, Loops or Customer.io, the head-to-head pages go deeper on the event-API trade-offs.
Related guides
Questions about building sequences on an email API
Can I build a drip sequence on a pure sending API?
Yes, but you own the whole machine: a table of people and the step each one is on, a timer that finds due steps, a suppression and unsubscribe check before every send, and exit rules that cancel whatever is still pending. Per-message scheduling only helps for short gaps, because SendGrid's send_at stops at 72 hours, SparkPost's start_time at 3 days and Mailgun's o:deliverytime at 3 or 7 days depending on plan. A week-two step therefore waits in your own queue until it is due.
Which API-first service runs the sequence for me?
Three of the services here do: Resend (Automations started by Send Event), Loops (workflows started by its events/send endpoint) and Customer.io (Automations fed by the Track API). SendGrid, Mailjet, Brevo and Elastic Email also run drip series, but you build those in the dashboard and your code mostly feeds them lists. Postmark, Amazon SES, Mailgun, SparkPost, Mandrill, SMTP2GO and MailerSend document no sequence object, so the timing there is your code.
How do I stop a sequence when someone upgrades or unsubscribes?
In an event-API tool you model it inside the workflow: Customer.io has exit criteria and goals, and Resend automations can branch on conditions or wait for an event. On a sending API you cancel what you queued: SendGrid lets you cancel or pause a batch by batch_id, Brevo lists delete calls for scheduled emails by batchId or messageId, and Resend has a Cancel Email endpoint. Whichever route you use, keep your own record of the person's state and check it again just before each send, because a cancel call and a due timer can race.
Is it safe to retry a send request that timed out?
Only where the vendor documents de-duplication. Resend accepts an Idempotency-Key of up to 256 characters and remembers it for 24 hours, Loops accepts up to 100 characters and answers 409 Conflict on a reused key within 24 hours, and Customer.io de-duplicates events that carry the same ULID id. Postmark's and SMTP2GO's send references show no equivalent option, so write the step as pending in your database before the call and mark it sent after, and build the key from the sequence, the step and the person so a retry cannot double-send.
What does a sequence stack cost at 10,000 contacts?
Event-API tools price by contact: Resend's Marketing slider reads $80 at 10,000 contacts, Loops is $99/mo at 10k and Customer.io Essentials reads $145 at 10k profiles. A pure sender prices by message: a four-email series to 10,000 people is 40,000 sends, which is 40 units of 1,000 at $0.16 each on Amazon SES Essentials, or the $10 Starter plan plus 30 extra units of 1,000 at $1 each on SMTP2GO, before you count the engineering time to build the scheduler, state and suppression logic.