resend/references/sending/overview.md
Version edbfece3f402.bb1 · MIT. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.
← Return to resource and package checksum
Sending Emails with Resend
Overview
Resend provides two endpoints for sending emails:
| Approach | Endpoint | Use Case |
|---|---|---|
| Single | POST /emails |
Individual transactional emails, emails with attachments, scheduled sends |
| Batch | POST /emails/batch |
Multiple distinct emails in one request (max 100), bulk notifications |
Choose batch when: - Sending 2+ distinct emails at once - Reducing API calls is important (by default, rate limit is 2 requests per second) - No attachments or scheduling needed
Choose single when: - Sending one email - Email needs attachments - Email needs to be scheduled - Different recipients need different timing
Quick Start
- Detect project language from config files (package.json, requirements.txt, go.mod, etc.)
- Install SDK (preferred) or use cURL — See ../installation.md
- Choose single or batch based on the decision matrix above
- Implement best practices — Idempotency keys, error handling, retries. See best-practices.md
Single Email
Endpoint: POST /emails (prefer SDK over cURL)
Required Parameters
| Parameter | Type | Description |
|---|---|---|
from |
string | Sender address. Format: "Name <[email protected]>" |
to |
string[] | Recipient addresses (max 50) |
subject |
string | Email subject line |
html or text |
string | Email body content |
Optional Parameters
| Parameter | Type | Description |
|---|---|---|
cc |
string[] | CC recipients |
bcc |
string[] | BCC recipients |
reply_to* |
string[] | Reply-to addresses |
scheduled_at* |
string | Schedule send time (ISO 8601) |
attachments |
array | File attachments (max 40MB total) |
tags |
array | Key/value pairs for tracking (see Tags) |
headers |
object | Custom headers |
topic_id* |
string | Scope email to a topic — if the recipient contact has opted out of this topic, the email is silently skipped |
*Parameter naming varies by SDK (e.g., replyTo in Node.js, reply_to in Python).
See single-email-examples.md for full SDK implementations with error handling and retry logic.
Batch Email
Endpoint: POST /emails/batch (prefer SDK over cURL)
Limitations
- No attachments — Use single sends for emails with attachments
- No scheduling — Use single sends for scheduled emails
- Atomic — If one email fails validation, the entire batch fails
- Max 100 emails per request
- Max 50 recipients per individual email in the batch
Pre-validation
Since the entire batch fails on any validation error, validate all emails before sending: - Check required fields (from, to, subject, html/text) - Validate email formats - Ensure batch size <= 100
See batch-email-examples.md for full SDK implementations with validation, chunking, and retry logic.
Large Batches (100+ Emails)
For sends larger than 100 emails, chunk into multiple batch requests:
- Split into chunks of 100 emails each
- Use unique idempotency keys per chunk:
<batch-prefix>/chunk-<index> - Send chunks in parallel for better throughput
- Track results per chunk to handle partial failures
See batch-email-examples.md for complete chunking implementations.
Deliverability
Follow these practices to maximize inbox placement.
For more help with deliverability, install the email-best-practices skill with npx skills add resend/email-best-practices.
Required
| Practice | Why |
|---|---|
| Valid SPF, DKIM, DMARC record | Authenticate the email and prevent spoofing |
| Links match sending domain | If sending from @acme.com, link to https://acme.com — mismatched domains trigger spam filters |
| Include plain text version | Use both html and text parameters for accessibility and deliverability |
| Avoid "no-reply" addresses | Use real addresses (e.g., support@) — improves trust signals |
| Keep body under 102KB | Gmail clips larger messages |
Recommended
| Practice | Why |
|---|---|
| Use subdomains | Send transactional from notifications.acme.com, marketing from mail.acme.com — protects reputation |
| Disable tracking for transactional | Open/click tracking can trigger spam filters for password resets, receipts, etc. |
Tracking (Opens & Clicks)
Tracking is configured at the domain level in the Resend dashboard, not per-email.
| Setting | How it works | Recommendation |
|---|---|---|
| Open tracking | Inserts 1x1 transparent pixel | Disable for transactional emails |
| Click tracking | Rewrites links through redirect | Disable for sensitive emails |
Configure via dashboard: Domain → Configuration → Click/Open Tracking.
To track different email types separately (e.g., tracking on for marketing, off for transactional), use separate subdomains.
Tags
Tags are key/value pairs that help you track and filter emails.
tags: [
{ name: 'user_id', value: 'usr_123' },
{ name: 'email_type', value: 'welcome' },
]
Constraints: Tag names and values can only contain ASCII letters, numbers, underscores, or dashes. Max 256 characters each.
Templates
Use pre-built templates instead of sending HTML with each request:
const { data, error } = await resend.emails.send({
from: 'Acme <[email protected]>',
to: ['[email protected]'],
subject: 'Welcome!',
template: {
id: 'tmpl_abc123', // or alias: 'welcome-email'
variables: {
USER_NAME: 'John', // Case-sensitive! Must match template exactly.
}
}
});
Cannot combine template with html, text, or react — mutually exclusive. See ../templates.md for full template management.
Testing
Avoid testing with fake addresses at real email providers — they bounce and destroy sender reputation.
| Method | Address | Result |
|---|---|---|
| Delivered | [email protected] |
Simulates successful delivery |
| Bounced | [email protected] |
Simulates hard bounce |
| Complained | [email protected] |
Simulates spam complaint |
| Your own email | Your actual address | Real delivery test |
Domain Warm-up
New domains must gradually increase sending volume to establish reputation.
New domain schedule:
| Day | Messages per day |
|---|---|
| 1 | Up to 150 |
| 2 | Up to 250 |
| 3 | Up to 400 |
| 4 | Up to 700 |
| 5 | Up to 1,000 |
| 6 | Up to 1,500 |
| 7 | Up to 2,000 |
Existing domain schedule:
| Day | Messages per day |
|---|---|
| 1 | Up to 1,000 |
| 2 | Up to 2,500 |
| 3–4 | Up to 5,000 |
| 5–6 | Up to 7,500 |
| 7 | Up to 10,000 |
Monitor: bounce rate < 4%, spam complaint rate < 0.08%.
Suppression List
Resend automatically manages a suppression list. Addresses are added when emails hard bounce or recipients mark as spam. Resend won't attempt delivery to suppressed addresses — the email.suppressed webhook event fires instead. Manage in Dashboard → Suppressions.
Notes
- The
fromaddress must use a verified domain - If the sending address cannot receive replies, set the
reply_toparameter - Node.js SDK supports
reactparameter for React Email components - Resend returns
{ error, data }— data is{ id: "email-id" }on success (single) or array of IDs (batch)