Zeabur EmailREST API Reference

REST API Reference

Zeabur Email provides a complete REST API that makes it easy to integrate email sending functionality into your application.

Authentication

All API requests must include an API key (Bearer Token format) in the HTTP header:

Authorization: Bearer zs_your_api_key_here

API keys can be created and managed in the Zeabur Email management page of the Zeabur console.

Base URL

https://api.zeabur.com/api/v1/zsend

Sending Emails

Send Single Email

Send an email immediately.

POST /emails

Request Body

{
  "from": "[email protected]",
  "to": ["[email protected]"],
  "cc": ["[email protected]"],           // Optional
  "bcc": ["[email protected]"],         // Optional
  "reply_to": ["[email protected]"], // Optional
  "subject": "Email Subject",
  "html": "<h1>HTML Content</h1>",
  "text": "Plain text content",
  "attachments": [                    // Optional
    {
      "filename": "document.pdf",
      "content": "base64_encoded_content",
      "content_type": "application/pdf"
    }
  ],
  "headers": {                        // Optional
    "X-Custom-Header": "value"
  },
  "tags": {                           // Optional
    "campaign": "newsletter",
    "user_id": "12345"
  }
}

Field Descriptions

FieldTypeRequiredDescription
fromstringYesSender email address, domain must be verified
toarrayYesList of recipient email addresses
ccarrayNoList of CC email addresses
bccarrayNoList of BCC email addresses
reply_toarrayNoList of reply-to addresses
subjectstringYesEmail subject (max 998 characters)
htmlstringNo*HTML format email content (max 5 MB)
textstringNo*Plain text email content (max 5 MB)
attachmentsarrayNoList of attachments (max 10, each up to 10 MB)
headersobjectNoCustom email headers (max 50)
tagsobjectNoCustom tags for categorization and tracking
⚠️

At least one of html or text must be provided. If both are provided, email clients will prioritize the HTML version, while clients that don’t support HTML will display the plain text version. Total recipients (to + cc + bcc) cannot exceed 50.

Response

{
  "id": "696de2c84210d814d66ee052",
  "message_id": "",
  "status": "pending"
}

Example

curl -X POST https://api.zeabur.com/api/v1/zsend/emails \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Welcome to Zeabur Email",
    "html": "<h1>Welcome!</h1><p>Thank you for using Zeabur Email.</p>"
  }'

Send Email with Attachments

Attachment content must be Base64 encoded:

// JavaScript example
const fs = require('fs');
 
const fileContent = fs.readFileSync('document.pdf');
const base64Content = fileContent.toString('base64');
 
const response = await fetch('https://api.zeabur.com/api/v1/zsend/emails', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    from: '[email protected]',
    to: ['[email protected]'],
    subject: 'Attachment Test',
    html: '<p>Please see the attachment</p>',
    attachments: [{
      filename: 'document.pdf',
      content: base64Content,
      content_type: 'application/pdf'
    }]
  })
});

Scheduled Sending

Schedule Email

Schedule an email to be sent at a specified time.

POST /emails/schedule

Request Body

Same as single email sending, with an additional scheduled_at field:

{
  "from": "[email protected]",
  "to": ["[email protected]"],
  "cc": ["[email protected]"],              // Optional
  "bcc": ["[email protected]"],            // Optional
  "reply_to": ["[email protected]"],  // Optional
  "subject": "Scheduled Email",
  "html": "<h1>HTML Content</h1>",
  "text": "Plain text content",
  "attachments": [],                     // Optional
  "headers": {},                         // Optional
  "tags": {},                            // Optional
  "scheduled_at": "2026-01-25T10:00:00Z" // Required, ISO 8601 format
}

The scheduled_at time must be in the future. All other fields follow the same rules as single email sending.

Response

{
  "id": "696de2c84210d814d66ee053",
  "status": "enqueued"
}

List Scheduled Emails

GET /emails/scheduled

Query Parameters

ParameterTypeDescription
pagenumberPage number (default 1)
limitnumberItems per page (default 20, max 100)
statusstringFilter by status: scheduled, sent, cancelled

Response

{
  "scheduled_emails": [
    {
      "id": "696de2c84210d814d66ee053",
      "from": "[email protected]",
      "to": ["[email protected]"],
      "subject": "Scheduled Email",
      "scheduled_at": "2026-01-25T10:00:00Z",
      "status": "enqueued",
      "created_at": "2026-01-20T08:00:00Z",
      "attempts": 0
    }
  ],
  "total_count": 1
}

Get Scheduled Email Details

GET /emails/scheduled/:id

Cancel Scheduled Email

DELETE /emails/scheduled/:id

Response

{
  "success": true,
  "message": "Scheduled email cancelled"
}

Batch Sending

Send Batch Emails

Send a large number of emails at once, with each email having different recipients and content.

POST /emails/batch

Request Body

{
  "emails": [
    {
      "from": "[email protected]",
      "to": ["[email protected]"],
      "cc": ["[email protected]"],       // Optional
      "bcc": ["[email protected]"],      // Optional
      "reply_to": ["[email protected]"], // Optional
      "subject": "Personalized Email 1",
      "html": "<p>Hello, User 1!</p>",
      "text": "Hello, User 1!",            // Optional
      "attachments": [],                   // Optional
      "headers": {},                       // Optional
      "tags": {"user_id": "1"}             // Optional
    },
    {
      "from": "[email protected]",
      "to": ["[email protected]"],
      "subject": "Personalized Email 2",
      "html": "<p>Hello, User 2!</p>"
    }
    // ... up to 100 emails
  ]
}

A single batch send supports up to 100 emails. Each email supports all the same fields as single email sending, including cc, bcc, reply_to, attachments, headers, and tags.

Response

{
  "job_id": "696de2c84210d814d66ee054",
  "status": "pending",
  "total_count": 2
}

Field Descriptions

FieldTypeDescription
job_idstringBatch job ID for querying status and details later
statusstringInitial job status (usually pending)
total_countnumberTotal number of emails in the batch job

job_id is the unique identifier for the batch job, different from single email id. Save this job_id to query batch sending progress and results later.

List Batch Jobs

GET /emails/batch

Query Parameters

Same as scheduled email list.

Get Batch Job Details

GET /emails/batch/:id

Response

{
  "job_id": "696de2c84210d814d66ee054",
  "total_count": 100,
  "sent_count": 95,
  "failed_count": 5,
  "status": "completed",
  "created_at": "2026-01-20T08:00:00Z",
  "completed_at": "2026-01-20T08:15:00Z"
}

Field Descriptions

FieldTypeDescription
job_idstringBatch job ID (consistent with the job_id returned at creation)
total_countnumberTotal number of emails in the job
sent_countnumberNumber of successfully sent emails
failed_countnumberNumber of failed emails
statusstringJob status: pending, processing, completed, failed
created_atstringJob creation time (ISO 8601 format)
completed_atstringJob completion time (optional)

Email Queries

List Emails

GET /emails

Query Parameters

ParameterTypeDescription
pagenumberPage number (default 1)
page_sizenumberItems per page (default 20, max 100)
statusstringFilter by status: pending, sent, delivered, bounced, complained

Response

{
  "data": [
    {
      "id": "696de2c84210d814d66ee052",
      "from": "[email protected]",
      "to": ["[email protected]"],
      "subject": "Test Email",
      "status": "delivered",
      "created_at": "2026-01-20T08:00:00Z"
    }
  ],
  "total": 1
}

Get Email Details

GET /emails/:id

Response

{
  "id": "696de2c84210d814d66ee052",
  "message_id": "0111019bd53de187-bda14a71-25a3-4fdc-a1a0-f543ff58c085-000000",
  "from": "[email protected]",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "reply_to": [],
  "subject": "Test Email",
  "html": "<h1>Test</h1>",
  "text": "Test",
  "status": "delivered",
  "mal_status": "healthy",
  "created_at": "2026-01-20T08:00:00Z",
  "headers": {},
  "tags": {},
  "attachments": []
}

Error Handling

The API uses standard HTTP status codes:

Status CodeDescription
200Request successful
400Bad request parameters
401Unauthorized (invalid or missing API key)
403Forbidden (insufficient permissions or account suspended)
404Resource not found
429Too many requests (rate limited)
500Internal server error

Error Response Format

{
  "error": "Brief error description",
  "message": "Detailed error message"
}

Common Error Examples

Validation Error (400)

{
  "error": "validation error",
  "message": "subject length (1200) exceeds maximum (998 characters)"
}

Authentication Error (401)

{
  "error": "unauthorized",
  "message": "Invalid or missing API key"
}

Permission Error (403)

{
  "error": "permission denied",
  "message": "API key does not have permission to send from this domain"
}

Quota Exceeded Error (429)

{
  "error": "daily quota exceeded (55/55), resets at 2026-01-23 00:00:00"
}

Description:

  • Status code: 429 Too Many Requests
  • Error message includes current usage, total quota, and reset time
  • Users can still query historical emails but cannot send new emails
  • Quota automatically resets daily at UTC 00:00

Limits

Daily Quota Limit

Each user account has a daily sending quota (Daily Quota), which is the most important limit:

Account LevelDefault Daily QuotaDescription
New User100 emails/dayInitial quota after account creation
Verified1,000 emails/dayAutomatically increased after domain verification

Quota Reset Time: Automatically resets daily at UTC 00:00

When Quota is Exceeded:

  • Returns status code: 429 Too Many Requests
  • Error message: daily quota exceeded (current/total), resets at time
  • Read operations still available: Can continue querying email history and details
  • Send operations rejected: Includes single, scheduled, and batch sending

Content Limits

  • Recipient count: Max 50 recipients per email (to + cc + bcc)
  • Email size: Max total size 10 MB (including attachments and headers)
  • Attachment count: Max 10 attachments
  • Single attachment size: Max 10 MB
  • Subject length: Max 998 characters
  • HTML content: Max 5 MB
  • Text content: Max 5 MB
  • Batch sending: Max 100 emails per batch

User Status Limits

User account status affects API access:

StatusAPI AccessSend EmailQuery EmailDescription
healthyNormal status, can send emails
reviewUnder review, can still send (quota may be reduced)
paused❌ (403)Suspended, can query but cannot send
banned❌ (401)Banned, completely prohibited from accessing API

Account Suspension/Ban Reasons:

  • Bounce Rate ≥ 4.0%
  • Complaint Rate ≥ 0.08%
  • Violation of terms of service

Status Descriptions:

  • review (Under Review): Email quality issues detected, system automatically marks for review. If there’s no further malicious behavior within 24 hours, may automatically recover to healthy status.
  • paused (Suspended): Temporarily prohibited from sending, but can still query historical data, manage domains, etc. Need to contact support team to appeal, fastest recovery 24 hours after approval.
  • banned (Banned): Completely prohibited from accessing API, including query operations. This status is manually set by staff, indicating serious violations, and is generally not recoverable.
⚠️

Requests exceeding limits will return a 400 Bad Request error. Quota exceeded returns 429 Too Many Requests.


Best Practices

1. Error Retry

For temporary errors (like 500 or 429), it’s recommended to use exponential backoff retry:

async function sendEmailWithRetry(emailData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch('https://api.zeabur.com/api/v1/zsend/emails', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer YOUR_API_KEY'
        },
        body: JSON.stringify(emailData)
      });
      
      if (response.ok) {
        return await response.json();
      }
      
      if (response.status === 400 || response.status === 403) {
        // Client errors, don't retry
        throw new Error(await response.text());
      }
      
      // Other errors, retry after waiting
      if (i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
      }
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

2. Use Batch Sending

When sending multiple emails, use the batch sending interface instead of looping through single sends:

// ✅ Recommended
await fetch('https://api.zeabur.com/api/v1/zsend/emails/batch', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({
    emails: users.map(user => ({
      from: '[email protected]',
      to: [user.email],
      subject: `Hello, ${user.name}`,
      html: `<p>Personalized content</p>`
    }))
  })
});
 
// ❌ Not recommended
for (const user of users) {
  await fetch('https://api.zeabur.com/api/v1/zsend/emails', {
    // ... single send
  });
}

3. Use Webhooks

Configure Webhooks to receive real-time email status updates instead of polling the query interface. See Webhook Configuration for details.

4. Use Tags Appropriately

Use the tags field to mark emails for easier tracking and analysis:

{
  "tags": {
    "campaign": "welcome_series",
    "user_segment": "new_users",
    "template_version": "v2"
  }
}