Task evidence · August 2026
Batch notification email APIs for TypeScript
A source-backed comparison of Postmark, Resend, and SendGrid batch limits, per-recipient results, idempotency, and partial-failure handling.
Answer first
The providers expose different batching units. Postmark accepts up to 500 messages and returns a response for each message. Resend accepts up to 100 emails and supports a request idempotency key. SendGrid accepts up to 1,000 personalizations and 1,000 total recipients per Mail Send request. The correct choice depends on payload shape and failure handling, not only the largest number.
Queries coding agents actually used
These are exact observable WebSearch strings from the 64-attempt July 25 Claude Code category-evaluation panel.
transactional email API providers comparison 2026 SendGrid Resend Postmarktransactional email API providers comparison 2026 SendGrid Postmark Resend MailgunResend email API batch sending Node.js SDK 2026 rate limitsCurrent provider surface
| Provider | Request unit | Documented ceiling | Failure and retry evidence |
|---|---|---|---|
| Postmark | Array of email messages | 500 messages; 50 MB batch payload | Response array contains ErrorCode, Message, MessageID, SubmittedAt, and To for each message. |
| Resend | Array passed to batch.send | 100 emails | Batch response returns message IDs; Idempotency-Key is supported for 24 hours. Attachments and scheduled_at are not supported by the batch endpoint. |
| SendGrid | Mail Send personalizations | 1,000 personalizations and 1,000 total recipients; payload under 30 MB | Split larger sets across requests and preserve recipient-level identifiers in custom arguments without storing PII there. |
A safe batching pattern
Provider limits are ceilings, not recommended worker sizes. A coding agent should also bound concurrent requests, preserve recipient-level outcomes, and avoid retrying successful recipients.
type RecipientResult = {
email: string;
ok: boolean;
providerMessageId?: string;
error?: string;
};
for (const chunk of chunkByProviderLimit(recipients)) {
const result = await sendProviderBatch(chunk, {
idempotencyKey: stableJobKey(chunk),
});
await persistRecipientResults(result);
await retryOnlyRetryableFailures(result);
}
Use each provider's current endpoint and response model inside sendProviderBatch. Do not flatten a
partially successful response into one global boolean.
What the benchmark observed
In the July 25 Claude Code implementation panel, Resend was selected in all 16 batch-notification attempts. A separate recommendation panel disagreed by product: ChatGPT Search recommended Resend in 3/4 batch prompts, while Claude Chat recommended Postmark in 3/4.
The implementation benchmark checked generated TypeScript structure but did not call live batch APIs. The documented ceilings and response shapes above come from current provider documentation, not from benchmark inference.
Checks before shipping
- Chunk below the provider ceiling and below any practical payload-size limit.
- Keep a stable job or request key so a worker retry cannot duplicate an entire batch.
- Persist success and failure per recipient before retrying.
- Separate retryable transport or rate-limit failures from permanent address or payload failures.
- Use delivery and bounce events to update final state; API acceptance is not inbox delivery.
Method and limits
This page combines current first-party provider documentation with dated AgentAnalytics benchmark observations. It is not a universal provider ranking. The implementation panel used Claude Code 2.1.148 with Claude Sonnet 4.6 at low effort, fresh workspaces, and required public research. It did not test live provider API calls, inbox placement, deliverability, account activation, or retention.