---
title: Scaling Email Delivery
description: Best practices for high-volume email sending
---

## Email delivery at scale

Plunk is built on AWS SES and handles millions of emails. Follow these practices to maintain high deliverability and performance at scale.

## Deliverability best practices

### Use custom domains

Emails from custom domains have higher trust and better deliverability than shared domains.

**Setup:**
1. Go to **Settings > Domains**
2. Add your domain
3. Configure DNS records (DKIM, SPF)
4. Wait for verification

[Learn more about custom domains →](/guides/custom-domains)

### Warm up new domains

Start small and gradually increase volume.

This builds sender reputation with email providers.

### Clean your list regularly

Remove bounced and inactive contacts:

```javascript
// Get bounced contacts
const bounced = await fetch('{{API_URL}}/events?name=email.bounced&limit=1000', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

// Unsubscribe them
for (const event of bounced.data.events) {
  await fetch(`{{API_URL}}/contacts/${event.contactId}`, {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ subscribed: false })
  });
}
```

**When to clean:**
- Hard bounces: Immediately unsubscribe
- Soft bounces: After 3 attempts
- No engagement: After 6-12 months

### Segment your audience

Send relevant content to engaged users:

```javascript
// Create engaged users segment
{
  "name": "Engaged Users",
  "filters": {
    "operator": "OR",
    "conditions": [
      { "field": "data.lastOpenedAt", "operator": "greaterThan", "value": "{{90 days ago}}" },
      { "field": "data.lastClickedAt", "operator": "greaterThan", "value": "{{90 days ago}}" }
    ]
  }
}
```

Send campaigns to engaged segments for better rates.

## Tracking control

You can disable tracking in your project settings for privacy-focused audiences or to reduce email size.

**When tracking is disabled:**
- No tracking pixel (no open tracking)
- Links not rewritten (no click tracking)
- Smaller email size
- May improve deliverability for privacy-conscious audiences

**When to disable:**
- Regulated industries (healthcare, finance)
- Privacy-focused users
- Transactional emails where tracking isn't needed
- High-volume sends where analytics aren't critical

## Rate limits

### AWS SES limits

Plunk automatically queues emails to stay within limits. Large sends process in background.

### Increase limits

For higher throughput:
1. Maintain good sender reputation
2. Consistent sending volume
3. Low bounce/complaint rates
4. Request limit increase from AWS

### Batch operations

For bulk operations, use appropriate endpoints:

```javascript
// ✓ Good: Single request for multiple recipients
fetch('{{API_URL}}/v1/send', {
  method: 'POST',
  body: JSON.stringify({
    to: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
    subject: 'Update',
    body: 'Message'
  })
});

// ✗ Avoid: Multiple requests
for (const email of emails) {
  await fetch('{{API_URL}}/v1/send', {...}); // Sequential, slow
}
```

## Campaign targeting

### Dynamic filtering

Target audiences without creating segments:

```javascript
{
  "name": "Premium Launch",
  "audienceType": "FILTERED",
  "audienceFilter": {
    "operator": "AND",
    "conditions": [
      { "field": "data.plan", "operator": "equals", "value": "premium" },
      { "field": "data.signupDate", "operator": "greaterThan", "value": "2025-01-01" },
      { "field": "subscribed", "operator": "equals", "value": true }
    ]
  },
  "templateId": "template_id"
}
```

**Use FILTERED for:**
- One-time sends
- Testing targeting
- Very specific criteria

**Use SEGMENT for:**
- Repeated targeting
- Workflow triggers
- Segment analytics

## Segment membership tracking

For segments used in workflows, enable membership tracking:

```bash
curl -X POST {{API_URL}}/segments \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Active Premium Users",
    "filters": {...},
    "trackMembership": true
  }'
```

**Enable for:**
- Workflow trigger segments
- Lifecycle stage tracking
- Cohort analysis

**Disable for:**
- Large segments (100k+ contacts)
- Campaign-only segments
- Frequently changing segments

Membership updates run every 5 minutes in background.

## Performance optimization

### Cache contact data

For high-volume API sends, cache contact lookups:

```javascript
// Cache contact IDs locally
const contactCache = new Map();

async function getContactId(email) {
  if (contactCache.has(email)) {
    return contactCache.get(email);
  }

  const contact = await fetch(`{{API_URL}}/contacts?search=${email}`);
  contactCache.set(email, contact.id);
  return contact.id;
}
```

### Use webhooks for async processing

Instead of waiting for email sends:

```javascript
// Workflow with webhook for confirmation
{
  "steps": [
    { "type": "SEND_EMAIL", "config": {...} },
    {
      "type": "WEBHOOK",
      "config": {
        "url": "https://your-api.com/email-sent",
        "method": "POST",
        "body": {
          "contactId": "{{id}}",
          "emailId": "{{emailId}}"
        }
      }
    }
  ]
}
```

### Batch workflow triggers

Trigger workflows in batches instead of one at a time:

```javascript
// Batch event tracking
const events = users.map(user => ({
  email: user.email,
  event: 'welcome_campaign',
  data: { userId: user.id }
}));

// Send in parallel (respecting rate limits)
await Promise.all(
  events.map(event =>
    fetch('{{API_URL}}/v1/track', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(event)
    })
  )
);
```

## Monitoring

### Track key metrics

Monitor these regularly:

- **Bounce rate** — Should be < 2%
- **Complaint rate** — Should be < 0.1%
- **Open rate** — Industry average 15-25%
- **Click rate** — Industry average 2-5%

Monitor these metrics in your dashboard analytics page to track deliverability and engagement over time.

## Next Steps

- [Set billing limits](/guides/billing-limits) to control costs
- [Monitor analytics](/guides/analytics) for deliverability
- [Troubleshoot issues](/guides/troubleshooting) if problems arise
