Email tools

How to Buy Domains via API for Cold Email

Buy domains via API for cold email: check availability, register names, connect domains you own and set redirects with the Outreach2day API, with curl examples.

Outreach2dayPublished 7 min read
On this page

Cold email runs on separate sending domains, so a team that sends for many clients buys domains often. Doing it by hand means a registrar, a DNS host and a copy-paste of MX, SPF, DKIM and DMARC records for every name. This article shows how to buy domains via API with Outreach2day: check which names are free, register them, or connect domains you already own, then add a website redirect.

It assumes you have an API key and a workspace ID. If not, start with the cold email API overview, which covers keys and the Python helper used below. The curl examples use two shell variables:

export O2D="https://public.outreach2day.com"
export O2D_KEY="ot2d_your_key"

Why Buy Cold Email Domains Through an API

Sending domains are separate from your main domain so that a drop in reputation does not reach your company mail. The cold email infrastructure guide explains the setup. An API is worth it when you:

  • create the same domain set for each client workspace (each workspace needs its first order in the app before it can buy over the API);

  • generate candidate names in code and need to know which are free;

  • want a record of what was bought, when and for which workspace, in your own system.

With Outreach2day, the DNS records a bought domain needs for email are set up as part of the order, so the API call replaces both the registrar step and the DNS step.

Registrar API vs Cold Email Domain API

A registrar API (Namecheap, Cloudflare Registrar and others) gives you the domain. You still create the MX, SPF, DKIM and DMARC records, set up mailboxes and add a redirect. Here the purchase and the email DNS are one order, the redirect is one more call, and the domain appears in the same workspace as your mailboxes.

Step 1: Check Domain Availability

POST /check_domains takes a JSON body with a domains array and returns one status per name.

curl -X POST "$O2D/check_domains" \
  -H "Authorization: Bearer $O2D_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["acmemail.com", "tryacme.com"]}'
{
  "domains": [
    {"domain": "acmemail.com", "status": "free"},
    {"domain": "tryacme.com", "status": "taken"}
  ]
}

status

Meaning

free

Available to register now

taken

Already registered by someone

not_allowed

Cannot be bought through Outreach2day

The check does not return prices. A .com costs $13 a year and a .info $5 a year (see pricing); a card processing fee is added to each purchase. The examples here use .com.

Step 2: Register the Domains via API

POST /domains/purchase registers the domains and charges the card saved on the workspace immediately. Send only names that came back free: if any name in the list is unavailable, the whole request fails with 400 and DOMAINS_UNAVAILABLE, and the response lists the names in unavailable_domains.

curl -X POST "$O2D/domains/purchase" \
  -H "Authorization: Bearer $O2D_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": 123,
    "domains": ["acmemail.com"],
    "contact_info": {
      "first_name": "Anna",
      "last_name": "Berg",
      "real_address": "1 Market St",
      "city": "San Francisco",
      "state_province": "CA",
      "postal_code": "94105",
      "country": "US",
      "phone": "+14155550100",
      "email_address": "anna@acme.com",
      "organization_name": "Acme Inc",
      "job_title": "Founder"
    }
  }'

A successful purchase returns what was charged:

{
  "status": "success",
  "message": "Successfully initiated purchase of 1 domains",
  "domains_purchased": ["acmemail.com"],
  "total_charged_cents": 1370,
  "invoice_id": "in_..."
}

total_charged_cents includes the processing fee: $13.00 for one .com plus $0.70 in the example above.

Registrant Contact

contact_info is the registrant record for the domain. It is optional in the schema, but send your own: the registrant is the legal holder of the domain. When you send it, every field except email_address is required, including organization_name and job_title. If email_address is left out, the email of the API key's owner is used.

Payment Errors

Response

Cause

402 PAYMENT_METHOD_REQUIRED

The workspace has no saved payment method. Make the first order in the app.

402 CARD_DECLINED or PAYMENT_FAILED

The charge did not go through. Update the card in the app.

400 DOMAINS_UNAVAILABLE

At least one name is not free; see unavailable_domains.

There is no quote or confirm step: the call is the purchase. If the list comes from generated names or from an AI agent, have a person or a hard limit in your code approve it before the request goes out.

The endpoint has no idempotency key and does not remove duplicates, and the card is charged before registration finishes. So:

  • remove duplicate names from the list before you send it;

  • never retry a purchase that timed out: first check GET /domains (below) for the names, because they can still show as free at the registrar while registration runs.

Error codes are inside detail, for example {"detail": {"error": "DOMAINS_UNAVAILABLE", "unavailable_domains": [...]}}.

Step 3: Confirm the Domains in Your Workspace

Registration and DNS setup continue in the background after the purchase call returns. GET /domains lists the domains of a workspace with their status and mailboxes_count:

domains = api("GET", "/domains",
              params={"workspace_id": 123})
for d in domains:
    print(d["domain"], d["status"],
          d["mailboxes_count"])

The api() helper is defined in the API overview. To see the DNS records from outside, query them with dig:

dig +short MX acmemail.com
dig +short TXT acmemail.com
dig +short TXT _dmarc.acmemail.com

DKIM sits under a selector (<selector>._domainkey.acmemail.com), so it does not show up in these queries; the selector is in the DKIM-Signature header (s=) of a message sent from one of the domain's mailboxes.

The SPF, DKIM and DMARC test explains what each record should contain. Once mailboxes on the domain exist, start their warm-up with the email warmup API.

Connect Domains You Already Own

Domains registered elsewhere can be connected for free. Outreach2day creates a DNS zone for each one; you change the nameservers at your registrar, and the records are added once the zone is active.

POST /domains/transfer/checkout starts the flow. redirectDomain is the site the domains will redirect to; up to 250 domains per request.

curl -X POST "$O2D/domains/transfer/checkout" \
  -H "Authorization: Bearer $O2D_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": 123,
    "domains": ["acme-outreach.com"],
    "redirectDomain": "acme.com"
  }'

The response gives domainsCount. Then poll GET /domains/transfer/status?workspaceId=123 for the nameservers and progress:

{
  "domains": [
    {
      "domain": "acme-outreach.com",
      "status": "pending_transfer",
      "nameservers": ["...", "..."],
      "zoneActive": false,
      "errorMessage": null,
      "setupStage": "waiting_nameservers",
      "dnsReady": false
    }
  ]
}

setupStage moves through creating_zone, waiting_nameservers, configuring_dns and active, or error with an errorMessage. Set the nameservers at your registrar when they appear. The request is rejected with DOMAINS_ALREADY_EXIST if a domain is already in Outreach2day, and with INVALID_DOMAINS for malformed names.

A polling loop in Python:

import time

while True:
    s = api("GET", "/domains/transfer/status",
            params={"workspaceId": 123})
    stages = {d["domain"]: d["setupStage"]
              for d in s["domains"]}
    print(stages)
    if all(v in ("active", "error")
           for v in stages.values()):
        break
    time.sleep(300)

Nameserver changes can take hours to reach every resolver, so poll every few minutes, not every second (the API allows 300 requests a minute).

Redirect Sending Domains to Your Website

People who get a cold email often type the sender's domain into a browser. A redirect sends them to your real site. PATCH /domains/redirects sets one target for many domains:

curl -X PATCH "$O2D/domains/redirects" \
  -H "Authorization: Bearer $O2D_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": 123,
    "domains": ["acmemail.com", "acme-outreach.com"],
    "redirectDomain": "acme.com"
  }'

Editing DNS Records Over the API

POST /domains/{domain}/dns exists, but it replaces the domain's whole record set with the list you send. A request with only one record deletes the MX, SPF, DKIM and DMARC records that make the domain work for email. The matching read endpoint, GET /domains/{domain}/dns, is switched off at the moment (it returns 503), so you cannot fetch the current set through the API first.

Do not call it on a domain that sends email unless support has confirmed the full record set with you: dig cannot list every record of a zone, and DKIM sits under a selector. For redirects, use PATCH /domains/redirects instead.

FAQ

How many domains can I buy in one call?

The purchase endpoint has no documented per-request cap; the connect flow takes up to 250 per request. Buy in batches you can check afterwards.

How many mailboxes should each domain have?

That is a deliverability decision, not an API one. See how to plan domain and mailbox counts for a target volume.

Can I buy domains before my first order?

No. The workspace needs a saved payment method, which it gets from the first order in the app. After that, purchases over the API charge that card.

How much does a domain cost over the API?

The same as in the app: $13 a year for a .com and $5 for a .info, plus the card processing fee. total_charged_cents in the response is the exact amount charged.

Get Started

Create an account and make the first order in the app, then create a key on the API keys page and run the availability check above.

Get an API key

Free guide

Send 100,000 cold emails a month

How to send 100,000 cold emails a month while keeping quality, engagement and conversion rates high: domains, mailboxes, warm-up, leads, copy and scaling.

Step-by-step playbook · 19 min read
  • Email tools8 min read

    Cold Email API for Domains, Warm-Up and Replies

  • Email tools9 min read

    Cold Email MCP Server: Build One for Claude

See deliverability issues before they kill performance

Monitor mailbox health in real time, spot degradation early, and keep warmup, protection, and sending in one place.