---
title: "Batch sending"
description: "Send up to 100 distinct messages — schedule and cancel as a unit."
url: https://www.unitpost.com/docs/batch
section: Docs
updated: 2026-08-28
---
# Batch sending

## Batch sending

> Send up to 100 distinct messages — schedule and cancel as a unit.

POST to /api/v1/email/batch to send many distinct messages in one request. Each entry is its own message with its own recipient, subject, and data. This is not one message to many people — for that, use a campaign.

The body is a JSON array of message objects, or an object { emails: [...], scheduled_at: "<ISO>" } to schedule the batch. Validation is all-or-nothing: if any item is invalid, nothing is queued.

### Group, inspect, cancel

Read a live rollup with GET /api/v1/email/batches/{id}, and stop every still-scheduled message with POST /api/v1/email/batches/{id}/cancel. In the dashboard, batches appear under Emails → Batches (/dashboard/email/emails?status=batches).

**cURL**

```bash
# Schedule the whole batch
curl https://www.unitpost.com/api/v1/email/batch \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scheduled_at": "2026-07-01T15:00:00Z",
    "emails": [
      { "from": "you@yourdomain.com", "to": "a@example.com", "subject": "Hi", "html": "<p>…</p>" },
      { "from": "you@yourdomain.com", "to": "b@example.com", "subject": "Hi", "html": "<p>…</p>" }
    ]
  }'

# Cancel the whole batch before it sends
curl -X POST https://www.unitpost.com/api/v1/email/batches/$BATCH_ID/cancel \
  -H "Authorization: Bearer $API_KEY"
```

**Node.js**

```ts
import { Unitpost } from "unitpost";

const unitpost = new Unitpost(process.env.UNITPOST_API_KEY);

// Scheduled batch — fires as a unit, cancelable as a unit
const { data } = await unitpost.email.batch({
  scheduled_at: "2026-07-01T15:00:00Z",
  emails: [
    { from: "you@yourdomain.com", to: "a@example.com", subject: "Hi", html: "<p>…</p>" },
    { from: "you@yourdomain.com", to: "b@example.com", subject: "Hi", html: "<p>…</p>" },
  ],
});

// Cancel the whole batch before it sends
await unitpost.email.cancelBatch(data.id);
```

**Python**

```python
from unitpost import Unitpost

unitpost = Unitpost()  # reads UNITPOST_API_KEY

# Scheduled batch — fires as a unit, cancelable as a unit
batch = unitpost.email.batch({
    "scheduled_at": "2026-07-01T15:00:00Z",
    "emails": [
        {"from": "you@yourdomain.com", "to": "a@example.com", "subject": "Hi", "html": "<p>…</p>"},
        {"from": "you@yourdomain.com", "to": "b@example.com", "subject": "Hi", "html": "<p>…</p>"},
    ],
}).data

# Cancel the whole batch before it sends
unitpost.email.cancel_batch(batch["id"])
```

**Ruby**

```ruby
require "unitpost"

unitpost = Unitpost::Client.new  # reads UNITPOST_API_KEY

# Scheduled batch — fires as a unit, cancelable as a unit
batch = unitpost.email.batch({
  scheduled_at: "2026-07-01T15:00:00Z",
  emails: [
    { from: "you@yourdomain.com", to: "a@example.com", subject: "Hi", html: "<p>…</p>" },
    { from: "you@yourdomain.com", to: "b@example.com", subject: "Hi", html: "<p>…</p>" },
  ],
}).data

# Cancel the whole batch before it sends
unitpost.email.cancel_batch(batch["id"])
```

**PHP**

```php
require 'vendor/autoload.php';

use Unitpost\Client;

$unitpost = new Client(); // reads UNITPOST_API_KEY

// Scheduled batch — fires as a unit, cancelable as a unit
$batch = $unitpost->email->batch([
    "scheduled_at" => "2026-07-01T15:00:00Z",
    "emails" => [
        ["from" => "you@yourdomain.com", "to" => "a@example.com", "subject" => "Hi", "html" => "<p>…</p>"],
        ["from" => "you@yourdomain.com", "to" => "b@example.com", "subject" => "Hi", "html" => "<p>…</p>"],
    ],
])->data;

// Cancel the whole batch before it sends
$unitpost->email->cancelBatch($batch["id"]);
```

**Laravel**

```php
use Unitpost\Laravel\Facades\Unitpost;

// Scheduled batch — fires as a unit, cancelable as a unit
$batch = Unitpost::email()->batch([
    "scheduled_at" => "2026-07-01T15:00:00Z",
    "emails" => [
        ["from" => "you@yourdomain.com", "to" => "a@example.com", "subject" => "Hi", "html" => "<p>…</p>"],
        ["from" => "you@yourdomain.com", "to" => "b@example.com", "subject" => "Hi", "html" => "<p>…</p>"],
    ],
])->data;

// Cancel the whole batch before it sends
Unitpost::email()->cancelBatch($batch["id"]);
```

**Go**

```go
import (
    "context"
    "github.com/unitpostcom/unitpost-go"
)

client := unitpost.New() // reads UNITPOST_API_KEY

// Scheduled batch — fires as a unit, cancelable as a unit
batch, err := client.Email.Batch(ctx, map[string]any{
	"scheduled_at": "2026-07-01T15:00:00Z",
	"emails": []any{
		map[string]any{"from": "you@yourdomain.com", "to": "a@example.com", "subject": "Hi", "html": "<p>…</p>"},
		map[string]any{"from": "you@yourdomain.com", "to": "b@example.com", "subject": "Hi", "html": "<p>…</p>"},
	},
})

// Cancel the whole batch before it sends
client.Email.CancelBatch(ctx, batch["id"].(string))
```

**Java**

```java
import com.unitpost.Unitpost;

var unitpost = new Unitpost(); // reads UNITPOST_API_KEY

// Scheduled batch — fires as a unit, cancelable as a unit
var batch = unitpost.email.batch(java.util.Map.of(
    "scheduled_at", "2026-07-01T15:00:00Z",
    "emails", java.util.List.of(
        java.util.Map.of("from", "you@yourdomain.com", "to", "a@example.com", "subject", "Hi", "html", "<p>…</p>"),
        java.util.Map.of("from", "you@yourdomain.com", "to", "b@example.com", "subject", "Hi", "html", "<p>…</p>")
    )
));

// Cancel the whole batch before it sends
unitpost.email.cancelBatch(String.valueOf(((java.util.Map<?, ?>) batch).get("id")));
```

**Rust**

```rust
use unitpost::Unitpost;

let unitpost = Unitpost::new(); // reads UNITPOST_API_KEY

// Scheduled batch — fires as a unit, cancelable as a unit
let batch = unitpost.email().batch(serde_json::json!({
    "scheduled_at": "2026-07-01T15:00:00Z",
    "emails": [
        {"from": "you@yourdomain.com", "to": "a@example.com", "subject": "Hi", "html": "<p>…</p>"},
        {"from": "you@yourdomain.com", "to": "b@example.com", "subject": "Hi", "html": "<p>…</p>"}
    ]
})).await;

// Cancel the whole batch before it sends
unitpost.email().cancel_batch(batch["id"].as_str().unwrap()).await;
```

**.NET**

```csharp
using Unitpost;

var unitpost = new UnitpostClient(); // reads UNITPOST_API_KEY

// Scheduled batch — fires as a unit, cancelable as a unit
var batch = await unitpost.Email.Batch(new
{
    scheduled_at = "2026-07-01T15:00:00Z",
    emails = new[]
    {
        new { from = "you@yourdomain.com", to = "a@example.com", subject = "Hi", html = "<p>…</p>" },
        new { from = "you@yourdomain.com", to = "b@example.com", subject = "Hi", html = "<p>…</p>" },
    },
});

// Cancel the whole batch before it sends
await unitpost.Email.CancelBatch(batch.GetProperty("id").GetString());
```

> **Batch limitations:** Per-item scheduled_at and attachments are not supported in batch — schedule the batch as a unit, or send individually if you need per-message attachments.

## Related

- [Scheduling & canceling sends](https://www.unitpost.com/docs/canceling): Schedule for later and recall a send before it leaves.
- [Idempotency & retries](https://www.unitpost.com/docs/idempotency): Safely retry sends without duplicating mail.
- [Bounces, complaints & suppression](https://www.unitpost.com/docs/deliverability): How bad addresses are handled and where to review them.
