Send API & Batch Send API
MailLogic provides Send API and Batch Send API. Send a message with optional attachments, or submit separate personalized emails in one batch.
Before you send
Select your domain in the dashboard, open API, and generate an API key. Copy the full key when it is shown; it cannot be viewed again. Regenerating it replaces the previous key. Keep it on your server, outside browser code.
API access requires a plan that includes it. The From address must be an active MailLogic email account on the key’s domain, with SMTP enabled and a password set. Domain ownership and sending DNS must be verified.
Use Authorization: Bearer YOUR_API_KEY for every endpoint below. X-API-Key is also supported. Replace the key and all .example addresses before running examples. Python examples require pip install httpx.
Send API
POST /v1/send accepts form data, not a JSON body. Send text, HTML, or both. Repeat to_email to send the same message to multiple recipients.
Optional: add an Idempotency-Key header (1–128 characters). Use a new key for each new email, and reuse the same key, API key, and payload when retrying. A completed request returns its original result without sending or charging again. Without this header, each request is a separate send.
Ordinary customer Send API requests share an account-wide limit of one new SMTP submission per second across all API keys and domains, with no accumulated burst allowance. Requests sent too soon receive HTTP 429 (send_api.rate_limit_exceeded) and an integer Retry-After in seconds. Wait at least that long, then retry the same logical request with the same Idempotency-Key, API key, and payload. Rejections do not reserve a future slot or extend the wait; completed idempotent replays do not count as new submissions. This limits submissions, not delivered recipients: one request may contain up to 100 recipients.
| Field | Usage |
|---|---|
from_email | Required. Active sender on the API key’s domain. |
to_email | Required. One address per field; repeat for multiple recipients. |
subject | Required form field. Supply a subject or message content. |
text_contenthtml_content | Optional text and HTML bodies. |
display_name | Optional override. Omitted or blank uses the Accounts Display Name; if unset, the bare address is used. |
attachments | Optional file uploads; repeat for multiple files. |
# Optional: add --header "Idempotency-Key: welcome-email-001" to this command. curl --request POST https://api.maillogic.io/v1/send \ --header "Authorization: Bearer YOUR_API_KEY" \ --form-string "[email protected]" \ --form-string "display_name=MailLogic Support" \ --form-string "[email protected]" \ --form-string "subject=Hello from MailLogic" \ --form-string "text_content=Thanks for signing up."
import httpx
response = httpx.post(
"https://api.maillogic.io/v1/send",
headers={
"Authorization": "Bearer YOUR_API_KEY",
# "Idempotency-Key": "welcome-email-001", # Optional
},
data={
"from_email": "[email protected]",
"display_name": "MailLogic Support",
"to_email": "[email protected]",
"subject": "Hello from MailLogic",
"text_content": "Thanks for signing up.",
},
timeout=30,
)
response.raise_for_status()
print(response.json())
For an attachment, append --form "attachments=@./welcome.pdf" to curl. In Python, open the file with with open("welcome.pdf", "rb") as file: and pass files={"attachments": ("welcome.pdf", file, "application/pdf")} to httpx.post inside that block.
Example response: 200 OK
{
"status": "success",
"message": "Email sent from [email protected] to [email protected]",
"message_id": "[email protected]",
"from_email": "[email protected]",
"to_email": [
"[email protected]"
],
"accepted_recipients": [
"[email protected]"
],
"rejected_recipients": [],
"suppressed_recipients": [],
"recipient_count": 1,
"accepted_recipient_count": 1,
"rejected_recipient_count": 0,
"suppressed_recipient_count": 0
}
Batch Send API
Send separate personalized messages asynchronously using JSON. Each recipient has an email and a variables object. Reference values with {{variable_name}} in a required subject_template and at least one of text_template or html_template.
Save this payload as batch.json. The same file is used for preview and creation.
{
"from_email": "[email protected]",
"subject_template": "Your {{month}} invoice",
"text_template": "Hi {{name}}, your balance is {{amount}}.",
"html_template": "<p>Hi {{name}}, your balance is {{amount}}.</p>",
"recipients": [
{
"email": "[email protected]",
"variables": {
"name": "Alice",
"month": "September",
"amount": "$42"
}
},
{
"email": "[email protected]",
"variables": {
"name": "Bob",
"month": "September",
"amount": "$18"
}
}
]
}
1. Validate and preview
POST /v1/batch-send/preview validates the payload without sending. The response includes up to five rendered previews, warnings, sendable/suppressed recipient counts, and quota information.
curl --request POST https://api.maillogic.io/v1/batch-send/preview \ --header "Authorization: Bearer YOUR_API_KEY" \ --header "Content-Type: application/json" \ --data @batch.json
2. Create the batch
POST /v1/batch-send requires an Idempotency-Key header of 1–128 characters. Use a new key for each new batch. Retry the same payload with the same key to avoid creating a duplicate; a conflicting payload returns 409.
curl --request POST https://api.maillogic.io/v1/batch-send \ --header "Authorization: Bearer YOUR_API_KEY" \ --header "Content-Type: application/json" \ --header "Idempotency-Key: invoices-2026-09-001" \ --data @batch.json
import json
import httpx
with open("batch.json", encoding="utf-8") as file:
payload = json.load(file)
base_url = "https://api.maillogic.io/v1/batch-send"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
response = httpx.post(
base_url,
headers={**headers, "Idempotency-Key": "invoices-2026-09-001"},
json=payload,
timeout=30,
)
response.raise_for_status()
batch_id = response.json()["batch"]["id"]
status = httpx.get(f"{base_url}/{batch_id}", headers=headers, timeout=30)
status.raise_for_status()
print(status.json())
A successful request returns 202 Accepted with batch, duplicate, and quota. Read batch.id to check progress. duplicate: true means the existing batch was returned.
3. Check progress
curl https://api.maillogic.io/v1/batch-send/BATCH_ID \ --header "Authorization: Bearer YOUR_API_KEY"
Replace BATCH_ID with batch.id. GET /v1/batch-send/{batch_id} returns the batch object directly. Poll periodically for status and submission counts (sent_count, failed_count, unknown_count), plus delivery outcomes: accepted_count, deferred_count, bounced_count, delivery_failed_count, pending_count, and suppressed_count.
Batch size depends on your plan; check the dashboard API section for your current limit. Batch Send API uses the sender’s saved Display Name at creation time and does not accept attachments or a request-level display_name.
Credits for all sendable recipients are reserved before the batch starts. If credits are insufficient, the batch waits without partially sending. Purchase credits or enable optional automatic top-up in Billing. Suppressed recipients use no credits.
Responses and troubleshooting
An HTTP success response confirms submission, not final delivery. For Send API, inspect status and the accepted, rejected, and suppressed recipient arrays and counts. partial_success means some recipients were rejected or suppressed; suppressed means none were submitted. Batch submission completion also does not mean all recipients received the email.
For errors, read the response’s detail (a message, structured error, or validation list). Check the key and plan for authentication/access errors, sender and DNS setup for authorization errors, and fields or template variables for 422. For Send API, 402 means more credits are needed; retry after payment succeeds. For batch 409 conflicts, check the idempotency key and payload; for 429, slow down requests.
For Send API retries, keep the same Idempotency-Key, API key, and payload. Reusing a key with different content returns 409 idempotency_key_conflict. For request_in_progress or submission_recording_pending, wait as directed by Retry-After and retry with the same key. If you receive submission_outcome_unknown, investigate the existing request; do not resend with a new key. A replayed successful result includes Idempotency-Replayed: true.
See email delivery troubleshooting for delivery outcomes and suppression.
Get your API key
Open API in your dashboard to get started.