To warm up an inbox, authenticate its domain, then send a few real one-to-one emails a day and raise the count in steps. Hold each step until bounces stay near zero. The schedule is simple. The list you send to decides whether it works.
This guide covers one mailbox. If you are warming a whole domain with several mailboxes, read domain warm up instead. LeadOcean does not send email or warm mailboxes. It builds the list and tells you how deliverable each address is.
TL;DR
- Takes about 30 minutes to set up and three weeks to ramp. Daily work is uploading one small file and checking your bounce count.
- Costs 0 records to size the list. The 150-contact example costs about 150 records on Solo ($97 a month) with a bulk export. On Free it costs about 300, through search and enrich.
- Google tells senders to raise volume gradually and watch spam rate and reputation (Google, October 2026). The numbers below are our starting plan, not an official schedule.
Prerequisites
- One new mailbox on a domain you control, and DNS access for that domain.
digon your machine.- A LeadOcean API key exported as
$LEADOCEAN_API_KEY. Get one free at app.leadocean.io. - Python 3 for the file splitter in Step 4.
- A sender that imports a CSV and enforces a daily cap, or a mail client you will send from by hand.
Step 1: Authenticate before the first send
Google requires SPF or DKIM from all senders. Senders of 5,000 or more messages a day need SPF, DKIM and DMARC (Google, October 2026). A new inbox should have all three from day one.
dig +short TXT acme-outbound.com | grep spf
dig +short TXT selector1._domainkey.acme-outbound.com
dig +short TXT _dmarc.acme-outbound.comStart DMARC in monitor mode so failures get reported before they get enforced.
v=DMARC1; p=none; rua=mailto:dmarc@acme-outbound.comSending to personal Gmail accounts needs a DKIM key of 1024 bits or longer. Google recommends 2048 where your provider supports it (Google, October 2026). Send one test message to an address you own and read the headers. Unauthenticated mail can be marked as spam or rejected with a 5.7.26 error (Google, October 2026).
Step 2: Size a verified-only list for free
Count before you pull anything. A count costs no records.
A new inbox has no history, so the first bounce counts for more than it will later. Filter emailStatus to verified. The default count also includes catch_all_valid and catch_all, which you leave out for now.
From Claude, call the free MCP tool:
leadocean_count_leads
jobLevel: ["Director"]
jobFunction: ["Sales & Business Development"]
country: ["US"]
emailStatus: ["verified"]
facets: falseOr from REST, send count=true as a query parameter with limit=1:
curl -s "https://api.leadocean.io/v1/people/search?count=true&limit=1" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-X POST \
-d '{
"jobLevel": ["Director"],
"jobFunction": ["Sales & Business Development"],
"country": ["US"],
"emailStatus": ["verified"]
}'meta.total is capped at 100,000. The response also carries meta.distinctCompanies, which tells you how many different employers sit behind the total. For a warm-up list you want that number high.
Step 3: Pull exactly the contacts the ramp needs
Pull 150 rows, not a round number. The ramp below sends 150 emails, so a larger pull wastes records. Every row is one record whatever columns you pick.
On Solo or above, use an export. Bulk CSV export is not on Free.
curl -s https://api.leadocean.io/v1/exports \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "inbox-warmup-us-sales-directors",
"limit": 150,
"preset": "full",
"caps": {"emails": 1, "phones": 1},
"filters": {
"jobLevel": ["Director"],
"jobFunction": ["Sales & Business Development"],
"country": ["US"],
"emailStatus": ["verified"]
}
}'Poll GET /v1/exports/{id} until status is done, then read downloadUrl. Each row costs one record, and the CSV headers come back as email_1_address and email_1_status.
On Free, search for people, then enrich each one with reveal_email:
curl -s https://api.leadocean.io/v1/people/enrich \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"person_id": "PERSON_ID_FROM_SEARCH", "reveal_email": true}'You can also use the Exports page in app.leadocean.io, which shows the record price before you start.
Step 4: Split the file into daily sends
Put the ramp in code so the daily batch is never a judgement call. One mailbox, weekdays only.
| Week | Sends a day | Sends in the week |
|---|---|---|
| 1 | 5 | 25 |
| 2 | 10 | 50 |
| 3 | 15 | 75 |
That is 150 emails. These numbers are illustrative. Mailbox providers publish no official schedule.
The script keeps only verified rows. It also sends to one contact per company domain. One bad employer cannot hit you twice.
import csv
RAMP = [5] * 5 + [10] * 5 + [15] * 5 # sends per weekday, 15 days
with open("inbox-warmup-us-sales-directors.csv", newline="") as f:
seen, rows = set(), []
for r in csv.DictReader(f):
addr = r["email_1_address"].strip().lower()
dom = addr.split("@")[-1]
if r["email_1_status"] == "verified" and addr and dom not in seen:
seen.add(dom)
rows.append(r)
i = 0
for day, per_day in enumerate(RAMP, start=1):
batch = rows[i : i + per_day]
i += per_day
if not batch:
break
with open(f"day-{day:02d}.csv", "w", newline="") as out:
w = csv.DictWriter(out, fieldnames=list(batch[0].keys()))
w.writeheader()
w.writerows(batch)
print(f"{i} contacts split into {day} daily files")Send at a steady time each day, not in one burst. Keep each message short, plain text and one question. Warm-up volume is small, so each email carries more weight than it will at scale.
Each enriched record also carries email_status, and each address in contact_emails has a verified_batch_date. That is the date of the verification run behind the verdict. A null means LeadOcean did not run the check itself. For a brand-new inbox, prefer rows that have a date, and skip the rest until the mailbox has history.
Step 5: Set the weekly gates
Move up a week only when the last one was clean. These gates are our rule of thumb, not a provider rule.
Advance a week when ALL of these hold:
hard bounces under 2% of the week's sends
spam complaints none visible in Postmaster Tools
auth failures none in the DMARC reports
replies steady, not falling
If any fails: repeat the week at the same volume.Google says to monitor server responses, spam rate and reputation as you raise volume (Google, October 2026). It also says to send only to people who want your messages. Postmaster Tools is where you watch it. Keep sending on weekdays after week three.
Step 6: Widen the list in order
After week three, add 20 to 30 percent a week and re-check the gates. Widen the list in this order:
- Keep
emailStatus: ["verified"]and add a second country or job function. - Add
catch_all_validonce bounces sit under the gate. - Add
catch_alllast, in its own batch, so you can read its bounce rate alone.
Re-run the Step 2 count for each new slice. It is free, and it shows whether the slice is big enough to be worth a batch.
Before you go past week three, check the sender side too. Keep the mailbox on one device or one sending tool, and do not change the From name mid-ramp. Consistency is cheap and it removes a variable when a gate fails.
What you get
The Step 2 count, run on 2026-10-21 with the free leadocean_count_leads tool. The filter was verified emails only. This counts people whose email status is verified, not everyone matching the level and country. Search applies the status per person. Treat it as an upper bound and confirm each address on export or enrich.
{
"total": 75872,
"totalIsExact": true,
"distinctCompanies": 33152,
"credits": 0
}That is 75,872 people with a verified email across 33,152 employers for the filter US, Director, Sales & Business Development. The 150-contact ramp uses a small slice of it, and the count cost nothing.
The split from Step 4 prints one line and writes up to 15 files. The shape, with a placeholder run:
$ python3 split.py
150 contacts split into 15 daily files
$ wc -l day-01.csv day-15.csv
6 day-01.csv
16 day-15.csvEach file has a header row, so day 1 holds 5 sends and day 15 holds 15.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | Missing or wrong x-api-key header. | Re-export $LEADOCEAN_API_KEY from app.leadocean.io and resend. |
| 429 | Requests above the plan's rate limit (5 a second on Free, 200 on Agency). | Wait for the Retry-After value and slow the loop. |
| 402 | Out of records. Free holds 1,000 for life. | Add a plan, or lower the export limit. |
| Zero results | A filter value is not an exact enum string, such as director where jobLevel wants Director. | List valid values with leadocean_list_enum_values or GET /v1/enums. |
| Export refused | The limit is above 50,000 rows a request, or the account is past 500,000 export rows in a day. | Lower limit, or split the pull across exports and days. |
| Fewer than 150 rows after the script | The one-contact-per-domain rule dropped duplicates. | Raise the export limit a little, or widen the filter. |
FAQ
How long does inbox warm-up take?
Plan on three weeks for one new mailbox at 5 to 15 sends a day. No provider publishes an official number. A mailbox with early bounces needs longer.
Do I need a warm-up tool?
No. A tool automates mailbox-to-mailbox traffic. You can warm an inbox with real one-to-one email to verified contacts and a slow ramp. A tool does not fix a bad list or missing authentication.
Why verified only?
A mailbox with no history has no reputation to absorb early bounces. The default count includes catch_all_valid and catch_all. Use emailStatus: ["verified"] for the first weeks, then widen it as in Step 6.
Does LeadOcean warm my inbox?
No. LeadOcean has no sending, sequencer or warm-up feature. It builds the list and returns each address's status. Pair it with a sender. See also inbox warming, domain warm up and Gmail warmup.
What does it cost?
Sizing is free. The 150-row export costs 150 records. Free gives you 1,000 records for life, but bulk export needs Solo at $97 a month. See pricing.
Build your warm-up list from verified addresses only
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →