Skip to content
View as Markdown

Batch sending ​

A batch sends the same message to many people, each getting their own email. You make one request; MarleyFetch works through the list in the background, pacing itself to your connection's quota.

Batch sending is a Pro feature.

Create a batch ​

POST /v1/send/batch needs content and recipients:

Content — pick one
templateInline { subject, text_body?, html_body? }.
template_idID of a saved template.
Recipients — pick one
recipientsArray of up to 1,000 addresses.
audience_idID of a saved audience. Enables personalisation.

Optional: name (shown in the dashboard) and from (which connection sends).

When each recipient needs different content, send an emails array instead.

To a list of addresses ​

bash
curl https://api.marleyfetch.com/v1/send/batch \
  -H "Authorization: Bearer $MARLEYFETCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "September newsletter",
    "from": "news@yourcompany.com",
    "template": {
      "subject": "What is new in September",
      "html_body": "<p>Here is what we shipped this month.</p>"
    },
    "recipients": ["alice@example.com", "bob@example.com"]
  }'
ts
const { batch_id } = await mf.batch()
  .name('September newsletter')
  .from('news@yourcompany.com')
  .subject('What is new in September')
  .html_body('<p>Here is what we shipped this month.</p>')
  .recipients(['alice@example.com', 'bob@example.com'])
  .send()

To an audience, personalised ​

When you send to an audience, {{placeholders}} in the subject and bodies are replaced with each member's payload:

json
{
  "name": "Renewal reminder",
  "template": {
    "subject": "{{first_name}}, your {{plan}} plan renews soon",
    "html_body": "<p>Hi {{first_name}}, your {{plan}} plan renews next week.</p>"
  },
  "audience_id": 12
}

A member added with "payload": {"first_name": "Alice", "plan": "Pro"} receives "Alice, your Pro plan renews soon". Placeholders are case-sensitive, and a variable the member does not have renders as an empty string — so a sentence like Hi {{first_name}}, degrades to Hi ,. Keep payloads complete for every variable you use.

Recipients passed as a plain recipients array have no payload, so leave placeholders out of those batches.

Different content per recipient ​

emails takes up to 1,000 fully formed emails — one recipient, subject and body each — and replaces both the content and the recipients:

json
{
  "name": "Order updates",
  "emails": [
    { "to": "alice@example.com", "subject": "Order 1042 has shipped", "text_body": "Your order is on its way." },
    { "to": "bob@example.com", "subject": "Order 1043 is delayed", "html_body": "<p>Sorry, your order is a day late.</p>" }
  ]
}

Each item goes to exactly one address; cc and bcc are rejected. For a handful of emails, calling /send for each works just as well.

The response ​

The request returns as soon as the batch is created:

json
{
  "success": true,
  "batch_id": 77,
  "total_count": 2,
  "message": "2 emails queued for batch delivery.",
  "status_url": "/v1/batches/77"
}

Track progress ​

Poll GET /v1/batches/{id}:

bash
curl https://api.marleyfetch.com/v1/batches/77 \
  -H "Authorization: Bearer $MARLEYFETCH_API_KEY"
json
{
  "success": true,
  "batch": {
    "id": 77,
    "name": "September newsletter",
    "status": "running",
    "total_count": 500,
    "sent_count": 320,
    "failed_count": 2,
    "progress": 64,
    "created_at": "2026-09-28 10:15:00",
    "updated_at": "2026-09-28 10:21:40"
  }
}

status moves from pending to running to completed (or failed). progress is the percentage of recipients handled, sent or failed. Once a minute is plenty for polling; the Batches page shows the same numbers, plus each failed recipient with its error and a retry button.

How pacing works ​

A batch never breaks your connection's limits:

  • It sends only as fast as the connection allows.
  • When the connection's quota is used up, the remaining emails wait for the next window and continue — a 5,000-recipient batch on a 2,000/day connection takes three days, and nothing fails.
  • If your provider starts rate-limiting, the batch backs off and resumes.

Only permanent problems, such as a deleted connection or an address your server refuses, count as failures.

Delete a finished batch ​

bash
curl -X DELETE https://api.marleyfetch.com/v1/batches/77 \
  -H "Authorization: Bearer $MARLEYFETCH_API_KEY"

Only completed or failed batches can be deleted; others return 400 INVALID_STATUS.

Errors specific to batches ​

StatusBodyCause
400BATCH_TOO_LARGEMore than 1,000 recipients or emails. Use an audience instead.
400{"error": "Invalid audience"}audience_id does not exist or is not yours.
400{"error": "Audience has no members"}The audience is empty.
404RESOURCE_NOT_FOUNDtemplate_id does not exist or is not yours.
403FEATURE_NOT_AVAILABLEBatches need the Pro plan.