Site icon APILayer Blog – All About APIs: AI, ML, Finance, & More APIs

How to Build an Email Verification Tool with the Mailboxlayer API (and Vibe Code It)

If you’ve ever run a sign-up form, you’ve seen what people type into the email field. Some of it is honest mistakes, like gmial.com or a missing letter in a name. Some of it is deliberate, like throwaway inboxes from disposable email services or test@test.com typed just to get past the form. Either way, the emails you send to those addresses bounce, your sending reputation takes the hit, and the person who made the typo never gets their confirmation link and assumes your product is broken.

A regular expression can’t catch most of this, because most bad addresses are perfectly valid in format. What you need is something that checks whether the domain accepts email and whether the specific mailbox exists, and that’s what an email verification API does. I recently built a verification tool, MailWiz, on the Mailboxlayer API. In this article I’ll go through the API first, then the logic that turns its results into something useful, then how I’d vibe code the whole tool with an AI assistant.

What does an email verification API check?

An email verification API runs a series of checks on an address without sending an email to it, and returns the result of each one. Mailboxlayer starts with the format, then looks for typos in the domain and suggests a correction when it finds one. It checks the domain’s MX records, which are the DNS entries that say which servers receive its mail, so a domain with no MX record can’t receive email at all.

The most useful check is the SMTP check. Mailboxlayer connects to the domain’s mail server and asks whether it would accept mail for that particular mailbox, then disconnects before anything is sent. This is what catches jhon.smith@company.com when the real address is john.smith. Some servers accept mail for every address on the domain, which makes the mailbox check meaningless, so paid plans also report whether the server is “catch-all”.

On top of those, you get three flags that describe the kind of address it is. role is true for shared inboxes like support@ or sales@, disposable is true for temporary inbox services, and free is true for free providers like Gmail and Outlook. Finally, there’s a score between 0 and 1 that combines everything into a rough measure of quality.

Those fields are useful well beyond a standalone verifier. You can block throwaway sign-ups, show a “Did you mean gmail.com?” hint under a form field, clean a mailing list before an email campaign, or treat leads with company addresses differently from free ones in a B2B sales flow.

How do you call the Mailboxlayer API?

You call Mailboxlayer with one GET request per address, passing your access key and the email as query parameters. The free plan gives you 100 checks a month, which is enough to build and test with. It isn’t licensed for commercial use, and catch-all detection starts on the Basic plan, according to Mailboxlayer’s pricing page. After signing up at mailboxlayer.com and copying your key, a check looks like this:

				
					curl "https://apilayer.net/api/check?access_key=YOUR_KEY&email=support@apilayer.com&smtp=1&format=1"
				
			

smtp=1 turns on the mailbox check, and format=1 pretty-prints the JSON, which is handy in a terminal and unnecessary in code. This is the response I got:

				
					{
  "email": "support@apilayer.com",
  "did_you_mean": "",
  "user": "support",
  "domain": "apilayer.com",
  "format_valid": true,
  "mx_found": true,
  "smtp_check": true,
  "catch_all": null,
  "role": true,
  "disposable": false,
  "free": false,
  "score": 0.64
}
				
			

The mailbox exists and the domain receives mail, so this address will get your email. The score is still only 0.64, because role is true. A shared support inbox is a perfectly good address for a business, but it isn’t a person, which matters if you’re collecting sign-ups or sales leads. Now compare it with an address that has a typo in the domain:

				
					curl "https://apilayer.net/api/check?access_key=YOUR_KEY&email=jane.doe@gmial.com&smtp=1&format=1"
				
			
				
					{
  "email": "jane.doe@gmial.com",
  "did_you_mean": "jane.doe@gmail.com",
  "format_valid": true,
  "mx_found": false,
  "smtp_check": false,
  "disposable": true,
  "free": true,
  "score": 0
}
				
			

The format is valid, so a regular expression would have accepted it. But gmial.com has no mail servers, the mailbox check fails, and the score is zero. The most useful field here is did_you_mean. If you show “Did you mean jane.doe@gmail.com?” under the field on a sign-up form, most people will click it, and you’ve saved a user who would otherwise never have received their confirmation email.

How do you turn the results into one verdict?

Most people looking at a verification result want one answer, so I group addresses into deliverable, risky and undeliverable. An address is undeliverable if its format is invalid, its domain has no MX record, or the SMTP check failed. It’s risky if it’s a disposable inbox, if the server is catch-all (so the mailbox check couldn’t confirm anything), or if the score is below 0.4. Everything else is deliverable.

One detail matters a lot here: smtp_check and catch_all can come back as null instead of true or false. For catch_allthat happens whenever catch-all detection isn’t part of your plan, as in the responses above, and either field can be nullwhen a check didn’t produce a result. Treat null as “unknown”, not as true or false. If your code does if (!data.smtp_check), it will treat every unknown result as a failure and mark perfectly good addresses as undeliverable. Keep the rule in one small function so it’s easy to test:

				
					export function verdict(d: MailboxlayerResponse) {
  if (!d.format_valid || d.mx_found === false || d.smtp_check === false) return "undeliverable";
  if (d.disposable || d.catch_all || d.score 
				
			

What are Mailboxlayer’s rate limits, and how do you handle them?

Mailboxlayer throttles requests that arrive too quickly, so a bulk verifier should check addresses one at a time and retry when it’s throttled. I found this out on the free plan while writing this article. I sent the two checks above one straight after the other, and the second came back with error 106, rate_limit_reached. After a pause of a few seconds, the same request went through.

When something goes wrong, Mailboxlayer returns a body of {“success”: false, “error”: {…}} with a code, a type and a message (an invalid key, for example, comes back with a 401 status). Read the error type from that body rather than relying on the status code alone. There are two limit errors to tell apart. rate_limit_reached means you’re sending too fast, and it’s worth retrying after a short wait that grows each time: 0.7 seconds, then 1.4, then 2.8. usage_limit_reached means your monthly quota is used up, and retrying will never work, so stop and say so.

Two more practical points. Bulk checking with the /bulk_check endpoint starts on the Professional Plus plan (25 addresses per request), so on the cheaper plans you loop over single checks from your own server. And the access key travels in the URL, so keep those calls on the server. A verifier that calls Mailboxlayer from browser JavaScript publishes your key to everyone who uses it.

How to vibe code the verifier

With the API understood, you can hand most of the build to an AI coding assistant. I’d use a tool that edits a real project, like Claude Code or Cursor, and give it one step at a time. As with any API integration, the AI will write code that looks right and gets the edge cases wrong, so the prompts below spell out the details from the sections above: the 200-status errors, the two kinds of limit errors, and running checks one at a time.

The first prompt builds the server route, which does all the real work:

				
					Create a Next.js App Router project with a POST route /api/verify that takes
{ emails: string[] }. De-duplicate case-insensitively, cap the batch at 100,
and call https://apilayer.net/api/check?access_key=...&email=...&smtp=1&format=0
for each address using MAILBOXLAYER_ACCESS_KEY from the environment. Run checks
one at a time (concurrency configurable via env, default 1). Mailboxlayer
errors have success:false and an error.type in the JSON body: retry rate_limit errors up to
4 times with exponential backoff starting at 700 ms, but never retry
usage_limit errors. Return one row per email, in input order, with either the
result or the error, plus counts of duplicates and truncated addresses.
				
			

To check it, send the route 101 addresses with a few duplicates mixed in and confirm that the duplicate and truncated counts add up. Every address costs a credit, so do this test with a handful of real addresses and a pile of obviously invalid ones.

The second prompt handles input. People paste addresses in every format imaginable, and they’ll also drop in CSV exports from their email tools:

				
					Build the page: a textarea that accepts one address, a comma or newline
separated list, or pasted CSV, plus a drop zone for .csv files. Extract every
token containing "@" (strip quotes and angle brackets) and show "N addresses
found" before the user clicks Verify. Keep malformed addresses in the list so
the API can report them as invalid instead of silently dropping them.
				
			

That last instruction is easy to miss. If your code throws away anything that doesn’t look like an email before calling the API, users won’t see which rows of their list were broken.

The third prompt builds the results, which is what people actually came for:

				
					Add an example sign-up form that calls /api/verify when the email field loses
focus. If did_you_mean is set, show a clickable "Did you mean ...?" link that
fills it in. Block disposable addresses with a friendly message. Never block
sign-up when the API fails or times out.
				
			

The final sentence of that prompt is the one to check by hand. Turn off your network or use an invalid key, and make sure the form still submits. Your verification service being down should never stop people from signing up.

Things to sort out before you launch

Credits are the first thing to manage, because every address costs one. Check the format locally before calling the API, de-duplicate, and cache results by address for a day or two so the same person re-submitting a form doesn’t cost you twice.

Privacy is the second. People will upload customer lists to a bulk verifier, so process them in memory and don’t store them unless you have a reason to. Say on the page what happens to the data, because the people with the biggest lists are the ones most likely to ask.

The third is how strict to be. Catch-all servers are common at companies, and a role address can be exactly what you want for a B2B product, so treat “risky” as a warning rather than a reason to reject someone. Finally, explain the checks on the page. A short section on what MX records, SMTP checks and catch-all servers mean helps users trust the results, and it answers the questions people search for when they’re deciding whether an address is real.

Wrapping up

An email verifier is a thin layer over one API call. You get the addresses in, respect the rate limits, turn a dozen fields into one clear verdict, and get the results back out as a CSV. Mailboxlayer does the hard part of talking to mail servers and keeping track of disposable domains, and an AI assistant can build the rest in an afternoon if you give it the details that matter up front.

You can try the finished version at apilayer.com/devtools/verify-email.

Explore APILayer Products

Discover APIs for geolocation, finance, aviation, weather, web scraping, AI, and more. Browse APILayer's product catalog to find the right APIs for your application and integrate the data and functionality your project needs.

Explore APILayer Products
Browse products by category and discover APIs for your project.
Find the right tools to start building.

STAY CONNECTED

Exit mobile version