Checking an address from your own code, without breaking the signup

You're building the registration flow and somebody has asked for email validation. The library takes ten minutes. The decisions around it are what determine whether this helps or quietly costs you users.

The pressure is unusual for an API call: it reaches out to a stranger's mail server, so its latency is set by infrastructure neither you nor your vendor controls. Any design treating it as a fast local call will break.

The order the work happens in

  1. Do the free checks in your own code first

    Shape, a typo suggestion, and an MX lookup on the domain. Those catch a large share of real errors, cost no credits, and return in milliseconds.

  2. Call the API outside the request path

    Accept the registration, then verify in a background job or a webhook. The person is never waiting on somebody else's SMTP server, which is the failure this design exists to avoid.

  3. Set a timeout and decide what it means

    Two seconds is generous for a check you aren't blocking on. When it expires, treat the result as unresolved rather than as invalid, because a timeout describes your network rather than the address.

  4. Store the result and the date, in separate columns

    One column for the verdict, one for when it was taken. Verification is evidence with a shelf life, and a boolean with no timestamp is worthless in six months.

  5. Handle rate limits and retries properly

    Back off exponentially on a 429 and make the job idempotent, so a retried request cannot charge you twice or write a second row.

What each result means here

The same four results and their flags, read against this job. A catch-all worth keeping in one situation is one to exclude in another.

ResultWhat to do with it
ValidStore it with the date. No user-facing effect.
InvalidPrompt for a correction on next login or by a side channel. Never delete the account.
Catch-allStore as unconfirmed. Business users land here routinely and they are usually your best accounts.
UnknownStore as unresolved with the reason, and re-check on a schedule. It is a statement about the server.
Role flagStore the flag. For a B2B product this is fine; for a per-person account it is worth a question.
Disposable flagThe only result worth blocking on, and only if a throwaway signup costs you money.

How you know it is finished

A registration completes in the same time it did before the integration existed, every account carries a verdict and a date, and no branch in your code turns an unresolved result into a rejection.

What this does not fix

Signup volume is low against list cleaning, so most products live inside the 100 free credits a month plus a $5 top-up for 1,000. Unknown results are not billed, which matters here because a real-time check produces more of them than a batch does.

Questions people ask

Should the API call block my signup form?

No. Run it in the background and fail open. Blocking a registration on somebody else's mail server policy converts their configuration into your lost customer.

What should the code do with an unknown result?

Accept the account, record the reason, and re-check later. An unknown means a server refused to answer, which is a fact about that server rather than about the person signing up.

How long should a verification result be trusted?

Store the date and decide per use. For a transactional address a few months is reasonable. Before you make a large marketing send to the same records, check again.

Try it on the file in front of you

100 free checks a month, no card. Addresses we could not get an answer on come back labeled as unknown, and those are not billed.