A reliable email verification integration treats submission, processing, and business decision as separate states. It sets timeouts, classifies errors before retrying, prevents duplicate jobs, keeps unknown results visible, and degrades according to an explicit product policy. The goal is not to force every address into valid or invalid. The goal is to preserve correct system behavior when networks and receiving mail servers are uncertain.
Mailthentic's single and bulk verification endpoints are asynchronous. A successful submission means the job was accepted, not that verification has finished. Production clients must store the job identity and poll the returned status and results URLs.
Use the verified Mailthentic contract
Send POST /api/verify/single with JSON containing one email. Authenticate server-side with Authorization: Bearer mt_..., or the supported X-API-Key header. An accepted single request deducts one credit and returns HTTP 202 with job_id, status_url, and results_url.
Use GET /api/jobs/{job_uuid}/status to observe progress and GET /api/jobs/{job_uuid}/results for paginated results. Bulk submission uses POST /api/verify/bulk with a CSV or XLSX multipart file. Use returned URLs where supplied so routing details remain centralized.
API-key callers require paid status and the correct scope. GET /api/me can test the credential without creating a verification or spending a credit.
Set separate timeout budgets
A connection timeout limits how long the client waits to establish a connection. A response timeout limits the HTTP exchange. A job deadline limits how long your product will wait for the asynchronous result before choosing a fallback. These values solve different problems.
Keep the initial HTTP timeout short enough to protect your application workers but long enough for normal request acceptance in your environment. Do not use the browser's patience as the verification job deadline. Continue polling from a background worker if the user interaction ends.
A timeout is an unknown submission outcome until proven otherwise. The server may have accepted the job even if the client did not receive the 202 response. Blindly repeating the POST can spend another credit and create duplicate work.
Classify HTTP responses before retrying
| Condition | Retry? | Production action |
|---|---|---|
| 400 malformed request | No | Fix validation or serialization and preserve the returned message |
| 401 authentication failure | No automatic retry | Check secret loading or rotation and alert the owner |
| 402 credit or paid-access error | No until account state changes | Route to billing or operations without blaming the address |
| 403 missing scope or permission | No automatic retry | Correct key scope or access policy |
| 413 request or job too large | No unchanged retry | Split according to current tier and job rules |
| 429 rate limited | Yes, later | Honor returned guidance, back off with jitter, and reduce concurrency |
| Transient 5xx | Usually bounded retry | Back off, monitor, and open the circuit when sustained |
| Connection or response timeout | Reconcile first | Check known job state before creating another submission |
The public API contract does not promise a fixed requests-per-second number. Treat 429 as the runtime authority. Do not hardcode an invented throughput claim into client behavior or documentation.
Handle 400 responses as engineering feedback
A 400 response indicates that the request did not meet the endpoint contract. Validate that the content type is JSON for single verification, the email field is present, and the payload shape is correct. For bulk, use multipart upload with a supported CSV or XLSX file.
Capture the status code, safe error code or message, endpoint, deployment version, and internal correlation ID. Do not retry the same malformed body. Alert on a sudden increase because it usually points to a deployment or schema change.
Separate authentication, subscription, and credit failures
A 401 points to a missing or invalid credential. A 403 can mean the key lacks the required scope. A 402 can indicate paid-access or credit state. These are operational account conditions, not properties of the submitted address.
Never expose the API key in client-side JavaScript, logs, analytics, screenshots, or error messages. Load it from the server's secret store. Test key rotation by supporting an overlap period or a controlled deployment, then revoke the old key.
Use exponential backoff with jitter
For an eligible transient failure, increase the delay after each attempt and add randomness so many workers do not retry together. Cap the delay and total attempts. Respect explicit server retry guidance when present.
A policy can be expressed as: next delay equals the smaller of a configured cap and a base delay multiplied by two to the attempt count, then adjusted by random jitter. The exact values belong in runtime configuration and load testing, not in article promises.
Retry reads more freely than submissions because GET requests should not create a new job. For POST reconciliation, first search your local state for an accepted job. If the submission outcome is unknown and the API has no client-supplied idempotency contract, prevent duplicate creation through your own state machine and controlled manual resolution.
Implement idempotency around business events
Mailthentic's documented single request contains an email field and does not publish a client idempotency-key field. Create an internal key from the stable business event, such as signup record ID plus workflow version. Store one submission record with states like pending-submit, accepted, polling, completed, failed-retryable, and failed-final.
Acquire a database lock or unique constraint before submitting. If the browser repeats a request, return the existing local state. Store the returned job_id before releasing the lock. This does not change the external API; it makes your caller idempotent.
Cache results with a clear freshness policy
Caching can avoid repeat checks when the same normalized address appears in a short period. Cache the result time, native status, reason, relevant flags, and verification mode. Key the cache by a protected normalized identifier rather than exposing raw addresses broadly.
Do not cache forever. Addresses and domains change. Set freshness by use case, source, and consequence, and bypass or refresh after a known bounce, address edit, domain change, or long inactivity. Never let a cached valid result override a later opt-out or suppression.
Treat unknown as a valid system state
Unknown can reflect temporary DNS or SMTP failures, greylisting, protected provider behavior, connection problems, or insufficient evidence. It should not become valid merely to keep the signup moving, and it should not become invalid merely to simplify a database column.
Store the reason and evidence. Route retryable causes to a bounded later check. Route persistently ambiguous, high-value cases to review. Let the product apply an explicit pending, restricted, fail-open, or fail-closed decision. The signup decision guide describes those user-facing choices.
Keep temporary SMTP evidence separate
A receiving server can defer recipient checks through greylisting, throttling, capacity controls, or policy. A temporary response is not proof that the mailbox is invalid. Repeated rapid probing can worsen the condition.
Preserve smtp_response_code, smtp_connectable, mailbox_confirmed, SMTP availability fields, and fallback_reason when returned. Use the overall status and reason rather than building a universal rule from one code.
Log for diagnosis without leaking contact data
Use a structured log with internal correlation ID, local workflow state, endpoint name, HTTP status, attempt number, elapsed time, provider job ID after acceptance, safe error classification, and deployment version. Redact authorization headers and avoid full email addresses in shared logs.
At the result layer, record the native status and reason in the protected application database. Do not copy full SMTP transcripts or confidence breakdowns into high-volume general logs unless the access and retention need is approved.
Measure the complete state machine
- Submission attempts, accepted jobs, and acceptance latency.
- Poll attempts, job completion time, and jobs beyond deadline.
- HTTP errors grouped by 400, 401, 402, 403, 413, 429, and 5xx.
- Connection and response timeouts by network path.
- Retry attempts, retry success, exhausted retries, and circuit state.
- Result distribution by source and verification mode.
- Unknown and temporary reasons without high-cardinality raw text labels.
- Duplicate submissions prevented by the local idempotency layer.
- Credits consumed compared with accepted business events.
Build service-level objectives around your integration, such as the proportion of accepted jobs reaching a terminal state within your tested deadline. Do not convert internal observations into public speed guarantees.
Alert on actionable conditions
Alert when authentication or scope failures appear, credit errors threaten the workflow, 429 or 5xx rates remain elevated, pending jobs age beyond the operating threshold, the circuit opens, or result distribution changes sharply for one source. Use a lower-severity notification for a small transient retry spike.
Attach a runbook: verify service health, check the account and key using the safe endpoint, inspect recent deployment changes, pause new submissions if needed, drain accepted jobs, and invoke the product fallback. Alerts without an owner and response path create noise.
Use a circuit breaker to protect both systems
A circuit breaker stops new external calls after a sustained failure threshold. While open, the application follows its approved fallback and periodically allows a limited probe. After successful probes, it closes gradually.
Do not open the circuit because addresses return invalid or unknown. Those are domain results, not service failures. Trigger on transport, server, authentication, or other operational signals selected by the owner. Keep already accepted jobs polling when safe, even if new submissions pause.
Choose fail-open or fail-closed by action
Fail-open may create a pending account or queue a contact without granting sensitive capability. Fail-closed may be appropriate for a narrowly defined high-risk event, but it couples that event's availability to verification. Decide per action and document the consequence.
Never show “invalid email” when the real cause is an API outage, missing credit, timeout, or open circuit. Tell the user the check could not complete, preserve their input, and offer the approved next step.
Safe production pseudocode
event = load_or_create_event(source_id, workflow_version)
if event.job_id:
return poll_known_job(event.job_id)
if circuit.is_open():
return apply_fallback(event, reason="verification_unavailable")
with lock(event.id):
if event.job_id:
return poll_known_job(event.job_id)
response = submit_single(email, timeout=CONFIGURED_TIMEOUT)
if response.status == 202:
save_job(event, response.job_id, response.status_url, response.results_url)
enqueue_poll(event.id)
elif is_retryable(response):
schedule_with_backoff_and_jitter(event)
else:
save_final_operational_error(event, response.safe_error)
The pseudocode is deliberately framework-neutral. Add transactions, secret management, request validation, safe serialization, observability, and tests that fit your stack. The critical behavior is reconciliation before resubmission and separation between an operational error and an address result.
Test failures before production traffic
Test malformed payloads, invalid and under-scoped keys, paid-access or credit errors, oversized bulk requests, 429, transient 5xx, connection reset, response timeout before and after acceptance, slow polling, terminal failed jobs, unknown results, duplicate browser submissions, worker restart, and circuit recovery. Confirm that no test leaks keys or personal data.
Start with the API playground for contract exploration and the API tutorial for introductory integration context. Production reliability begins when the happy path stops being the only tested path.
Build against asynchronous reality
Review the Mailthentic API contract, store accepted job identities, and test retry, timeout, and fallback behavior before routing production decisions.
Ready to verify your email list?
Start free with 50 verification credits. No credit card required.