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"
      }
    }
  }
}