API docs
Everything the app does, from your own code or AI assistant. Same credits, same evidence, same free unknowns.
Authentication
Create a key in the app under API & MCP and send it as a bearer token. Keys are shown once; revoke a key any time and it stops working right away.
Authorization: Bearer bo_1a2b3c4d5e.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Base URL: https://api.bounceowl.com
POST/v1/verify
Check one address live and get the result in the same response. Most checks take a few seconds; slow mail servers can take up to two minutes, so set your client timeout to at least 150 seconds.
curl https://api.bounceowl.com/v1/verify \
-H "Authorization: Bearer $BOUNCEOWL_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "maya.chen@lumenforge.io"}'{
"id": 4182,
"email": "maya.chen@lumenforge.io",
"verdict": "valid",
"reason": "",
"checked_at": "2026-11-06T14:02:11Z",
"mailbox": "accepts_mail",
"mail_server": "aspmx.l.google.com",
"provider": "google_workspace",
"gateway": "",
"method": "smtp",
"duration_ms": 2840,
"catch_all": false,
"disposable": false,
"role": false,
"free_provider": false,
"reused": false,
"charged": 1,
"catchall_status": "",
"catchall_verdict": ""
}If anyone on BounceOwl checked the same address in the last 7 days, that result is returned with its original checked_at, reused: true and charged: 0.
GET/v1/checks/{id}
Fetch a check you ran before, including the catch-all check result once it arrives.
curl https://api.bounceowl.com/v1/checks/4182 -H "Authorization: Bearer $BOUNCEOWL_KEY"
{ "id": 4182, "verdict": "catch_all", "catchall_status": "done", "catchall_verdict": "valid", ... }POST/v1/bulk
Upload a CSV or XLSX file (up to 50 MB, 500,000 rows). We find the email column, remove duplicates and estimate the cost. Nothing is charged yet.
curl https://api.bounceowl.com/v1/bulk \ -H "Authorization: Bearer $BOUNCEOWL_KEY" \ -F "file=@leads.csv" \ -F "email_column=Work Email"
{ "id": 311, "status": "preparing", "filename": "leads.csv", ... }email_column is optional. Poll GET /v1/bulk/{id} until status is ready to see rows_unique and credits_needed.
POST/v1/bulk/{id}/start
Reserve credits and start checking. Pass max_rows to check only the first rows. Unused credits are returned when the list finishes.
curl -X POST https://api.bounceowl.com/v1/bulk/311/start \
-H "Authorization: Bearer $BOUNCEOWL_KEY" \
-H "Content-Type: application/json" \
-d '{"max_rows": 10000}'{ "id": 311, "status": "running", "rows_to_check": 10000, "credits_reserved": 9874, ... }GET/v1/bulk/{id}
Progress and counts: rows_done, count_valid, count_invalid, count_catch_all, count_unknown, credits_charged.
curl https://api.bounceowl.com/v1/bulk/311 -H "Authorization: Bearer $BOUNCEOWL_KEY"
{ "id": 311, "status": "done", "rows_done": 10000, "count_valid": 7412, "count_invalid": 1690, "count_catch_all": 702, "count_unknown": 196, ... }GET/v1/bulk/{id}/results
Download results as CSV: your original columns plus bo_verdict, bo_reason, bo_mailbox, bo_mail_server, bo_provider, bo_catch_all, bo_role, bo_disposable, bo_checked_at and bo_catchall_check. Filter with ?verdict=valid.
curl "https://api.bounceowl.com/v1/bulk/311/results?verdict=valid" \ -H "Authorization: Bearer $BOUNCEOWL_KEY" -o valid.csv
Name,Work Email,Company,bo_verdict,bo_reason,...
POST/v1/catchall
Run the deeper catch-all check on results marked catch_all. Send one of email, check_ids (up to 1,000) or bulk_job_id. Answers arrive within minutes; read them with GET /v1/checks/{id}.
curl https://api.bounceowl.com/v1/catchall \
-H "Authorization: Bearer $BOUNCEOWL_KEY" \
-H "Content-Type: application/json" \
-d '{"check_ids": [4182, 4190]}'{ "requested": 2, "reused": 0, "charged": 10 }Unclear answers and checks with no answer after 30 minutes are refunded.
GET/v1/pricing
Current credit costs and packs. No key needed.
curl https://api.bounceowl.com/v1/pricing
{ "credits_per_check": 1, "credits_per_catchall_check": 5, "signup_credits": 100, "credits_expire": false, "packs": [...] }Verdicts
- valid
- The mailbox exists and accepts mail.
- invalid
- Sending would bounce. See reason: rejected, disabled, full_inbox, no_mail_server or syntax.
- catch_all
- The domain accepts every address, so a regular check can't tell. Run a catch-all check for a real answer.
- unknown
- The mail server gave no clear answer (timeout or block). Always free.
Errors
- 400
- The request is missing something or has a bad value. The body names the field and the problem, for example {"email": ["This field may not be blank."]}.
- 401
- The API key is missing, wrong or revoked.
- 402
- Not enough credits. The body has needed and balance.
- 404
- That check or list doesn't exist on your account.
- 429
- Too many requests. The default limit is 300 checks per minute per key.
{ "detail": "Not enough credits.", "code": "insufficient_credits", "needed": 1, "balance": 0 }MCP server
Let Claude Code, Cursor or another MCP client check addresses for you. Tools: verify_email, verify_emails (up to 20), start_list (up to 50,000), list_status, list_results, catchall_check and get_credits.
Claude Code
claude mcp add --transport http bounceowl https://api.bounceowl.com/mcp \ --header "Authorization: Bearer $BOUNCEOWL_KEY"
Cursor and other clients
{
"mcpServers": {
"bounceowl": {
"url": "https://api.bounceowl.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}