Docs
The Warmup API lets you manage email warmupⓘ sending accounts programmatically: add and remove from-addresses, set how aggressively each address warms up, and read deliverability statistics.
Everything is served over HTTPS under the base path /api/v1/warmup. Requests and responses are JSON, except for two CSV download endpoints.
All paths use hyphens. Underscored variants of the same name are not registered and return 404.
ZeroBounce provides the base URL for your account:
Throughout this document, the base URL is written as {BASE}. To build a full request URL, add the endpoint path to the end of it.
Every request must carry your product API key in the API-Key header:
Requests with a JSON body must also send Content-Type: application/json.
The examples in this document assume these shell variables:
Your key is bound to a single team. The API resolves the team from the key on every call, which is why no endpoint asks you to supply a team identifier. Where a schema still contains a team_id field, any value you send is replaced server side with the team the key belongs to.
The API uses two different error shapes, and which one you get depends on the kind of problem. Knowing the split makes error handling much simpler.
This is the distinction worth building into your client:
So {"engagement_rule_id": "two"} returns 422, while {"engagement_rule_id": 9} returns 400. Do not expect a detail array on a 400, or an error string on a 422.
Two names are used for the same thing across endpoints. Both refer to a from-address you have registered:
Where the address appears in a URL path rather than a query string, URL-encode the @.
All paths are relative to {BASE}/api/v1/warmup.
There are two onboarding models, chosen with the onboarding_model field on POST /sending-accounts.
You own the mailbox and its SMTP configuration. Warmup handles the sending schedule and engagement.
Required fields: from_address, onboarding_model, engagement_rule_id (0 to 4).
A 200 response echoes onboarding_model: self_managed and reminds you to configure SMTP on your side.
Useful follow-ups: POST /test-smtp-configuration to check connectivity, GET /check-domain-dmarc to verify domain records, and PUT /engagement-rule to change pace later.
ZeroBounce provisions and sends from the mailbox. You supply the company context used to generate warmup content, plus an optional sending schedule.
Required fields: from_address, onboarding_model: zb_managed. Optional: email_content, managed_sending. Any engagement_rule_id you send is ignored on this path.
A duplicate from-address returns 409.
The managed_sending block is write-only. There is no public endpoint to read it back or change it after creation.
PUT /sending-accounts changes the engagement rule, status and auto-enable flag on an existing address.
auto_enable is nullable in the schema but is required in practice. Sending null, or leaving the key out, returns 400 {"error": "Missing auto_enable parameter"}. Always send an explicit boolean.
auto_enable: true is only accepted on an account that is not currently active. Any status other than Paused or Disabled counts as active:
PUT /smtp-credentials is named for historical reasons. It does not store an SMTP host, username or password. What it does is remap an existing from-address to a new one, keeping the account and its history.
Returns 200 with an empty body. To test SMTP connectivity, use POST /test-smtp-configuration (section 8.3).
Returns 204. This is destructive and cannot be undone.
An engagement rule controls how recipients interact with warmup mail from an address: how often it is replied to, marked important, or clicked. Each address carries exactly one rule.
GET /engagement-rules returns the catalogue:
To change the rule on an address:
Returns 204. rule_id must be 0 to 4. -1 is rejected here, because a custom rule is created through the endpoints in section 7.2.
A custom rule replaces the standard rule with your own engagement percentages. Creating one sets the address to engagement_rule_id: -1.
Create
Returns 200 with can_update: false and remaining_seconds of roughly 604800. A second create on the same address returns 409.
Read
URL-encode the @ in the path.
Update
Same body as create, sent with PUT. Returns 200 once the cooldown has expired, 429 while it is still running, and 404 with "Use POST to create one" if the address has no custom rule yet.
Delete
Deleting a custom rule restores a standard rule, so the request has to say which one. engagement_rule_id here is the standard rule to fall back to, not the identifier of the custom rule. It must be 1 or higher, so 1 to 4 are the valid choices.
Returns 200 {"status":"ok","deleted":true}.
The rule identifier is checked before the address is looked up. A request that gets both wrong returns 400 for the rule, not 404 for the address.
After a delete, the address can have a new custom rule created immediately. That create starts a fresh 7 day cooldown.
An address with no history still returns 200, with zeros or empty lists rather than an error.
GET /seeds/download and GET /statistics/download return CSV as a Content-Disposition attachment. The seeds file is warmup_seeds.csv with columns seed,provider.
GET /check-domain-dmarc inspects DNS records. Despite the parameter name, from_address here takes a domain, not an email address, and is not email-validated. dkim_selector is optional. An empty domain returns 400.
Add &dkim_selector=default to check a specific DKIM selector.
POST /test-smtp-configuration checks that a set of SMTP credentials can connect and hand off a message. Nothing is stored.
A 200 {"message": "Test email sent."} means the SMTP server accepted the message. A failure means the credentials or server details are wrong.
Nine endpoints are limited to 100 requests per 60 seconds per IP address. Exceeding the limit returns 429.
The remaining endpoints are not rate limited today. Build your client to back off on 429 regardless, since limits may be extended to more routes in future releases.
A 429 on PUT /custom-engagement is not a rate limit. It is the 7 day cooldown described in section 7.2.
Your API key scopes every request to one team. You can only see and change from-addresses that belong to that team. Requests for an address outside it return a not-found response rather than another team's data.
Supplying a team_id in a request body does not change this. The value is always overwritten with the team resolved from your key.
For access credentials or questions about this API, contact your ZeroBounce account manager.