An email validation API is, at heart, a server-to-server request: you send an address with your private key and get back one answer per check that was run. What confuses people is rarely the call; it is the response. ByeBouncer does not return a “good or bad” verdict or a score. It returns what was checked, what could be confirmed and what stayed undetermined. This guide shows how to read each part so you can decide inside your own system.
Preparing an email validation API request
The endpoint is POST /api/v1/verify-email, with a JSON body {"email": "..."}. The key goes in the Authorization header as a Bearer token. For server-side validation use a bb_live_… key; bb_pub_… keys exist for the browser widget and are meant for that, not for your backend. For a response split by data point and without a score, add ?result_format=validation-v1 to the URL. Without it you keep the earlier format, retained for compatibility with existing integrations.
curl -X POST "https://api.byebouncer.com/api/v1/verify-email?result_format=validation-v1" \
-H "Authorization: Bearer $BB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"person@example.com"}'Each verification uses 1 credit, even when the result comes from cache. Keep that in mind for forms: validate once on submit, not on every keystroke. Errors are the usual ones: 400 for an invalid body, 401 for a missing or invalid key, 402 when credits run out and 429 when you exceed the request limit (with a Retry-After header).
Reading the result: deliverability and checks
A validation-v1 response includes execution_status (completed or partial) and a results.email block. Three things matter there. First is deliverability, which takes only three values: deliverable, undeliverable or undetermined. Second is checks, an object with one entry per check (syntax, the domain’s mail server, SMTP, disposable-domain catalog). Third is attributes, with descriptive data such as the inferred hosting provider or whether the address needs SMTPUTF8.
Each check has a state: completed, not_requested, not_applicable, unavailable or failed. That is the key difference from a yes/no API: an unavailable check does not mean the address is bad, it means that check could not be performed. When a reason is present it tells you why. For example, null_mx means the domain explicitly declares it does not receive mail, while a temporary DNS failure is reported as unavailable and never as “the mailbox does not exist”.
- undeliverable: the domain does not receive mail, the syntax is invalid or the server rejected that specific recipient. It is the only case where stopping without second thoughts makes sense.
- deliverable: the server accepted the recipient. That is a signal, not a guarantee of delivery, inbox placement or that anyone reads the message.
- undetermined: there is no basis to confirm or deny. Do not treat it as valid, and do not discard it by reflex.
A trimmed, fictional example of an undetermined address: deliverability arrives as undetermined, checks.syntax is completed with value true, checks.mail_server is completed with value true and checks.smtp is unavailable with the reason provider_blocked. Read together, it says something precise: the address is well formed and the domain receives mail, but the provider did not let that mailbox be confirmed. With a single “status” field that information would be lost, which is the underlying reason to look at the checks and not only at the verdict.
What to do with undetermined results
There are two usual causes. One is technical and transient: a timeout, a server answering with a temporary error, or greylisting (the server deliberately rejects the first attempt and accepts the retry). The SMTP standard, RFC 5321, separates 4xx temporary replies from 5xx permanent ones, and that distinction is why you retry some and not others. The other is the destination’s configuration: a catch-all server accepts any recipient, so its acceptance does not prove the mailbox exists, and some providers block SMTP probes without giving a useful answer.
In practice, a reasonable sign-up policy is: if undeliverable, ask the user to fix the address; if undetermined, accept the sign-up and send a confirmation email, the only real proof the person controls that mailbox; if deliverable, carry on. A bulk campaign can be different: many teams prefer to leave undetermined addresses out of the first send and retry later. That decision is yours; the API gives you evidence, not the rule.
Integrating without exposing the key
- Make the call from your backend. A bb_live_ key in browser JavaScript is one “view source” away from being public.
- Keep it in an environment variable or a secrets manager, never in the repository.
- Set a short timeout and decide what happens if the API does not answer: usually it is healthier to let the sign-up through and verify later than to block a user because of someone else’s outage.
- Honor Retry-After when you get a 429 and do not retry in a tight loop.
- Store the result with its observation date: an email’s validity changes over time and year-old data is not worth what it was.
What this API does not do
Better said plainly. ByeBouncer does not return a global score, nor does it decide whether someone is authentic, a bot or fraud. A server accepting the recipient does not prove delivery, ownership or consent. The disposable-domain catalog only detects exact matches against known lists: no match does not guarantee the address belongs to a real person. And domain data (SPF, DMARC, age) describes the domain, not the mailbox. With those limits clear, the result is useful for filtering junk and ordering priorities, not for replacing your own judgment.
See how email verification works →
Create an account and try the API →