Skip to Content
GuidesError Handling

Error Handling

The VerifNow API uses standard HTTP status codes and returns consistent response objects so you can handle every scenario gracefully.

Important: When a validation is successfully processed, the API always returns 200 OK — regardless of whether the email is valid or not. Check the valid, message, and deliverability fields in the response body to determine the validation outcome.


Successful validation responses (200 OK)

When the API processes your validation request, it returns 200 OK with a response body containing the result. The message field reflects the deliverability assessment:

Deliverabilitymessagevalid
DELIVERABLE"Valid email address"true
RISKY"Email address is valid but has risk factors"true
UNDELIVERABLE"Email address is unlikely to be deliverable"false
UNKNOWN"Unable to fully verify email deliverability"varies

Example: deliverable email

{ "valid": true, "message": "Valid email address", "normalizedValue": "john@gmail.com", "originalValue": "john@gmail.com", "validationLevel": "PREMIUM", "emailDetails": { "signals": { ... }, "risk_score": 5, "risk_level": "LOW", "deliverability": "DELIVERABLE", "applied_level": "PREMIUM" } }

Example: undeliverable email

{ "valid": false, "message": "Email address is unlikely to be deliverable", "normalizedValue": "user@gmal.com", "originalValue": "user@gmal.com", "validationLevel": "PREMIUM", "emailDetails": { "signals": { "syntax_valid": true, "mx_valid": false, "typo_detected": true, "suggested_domain": "gmail.com", ... }, "risk_score": 95, "risk_level": "HIGH", "deliverability": "UNDELIVERABLE", "applied_level": "PREMIUM" } }

A 200 response does not mean the email is valid. Always check the valid and deliverability fields in the response body.


Error response format

When the API cannot process your request (missing input, authentication failure, quota exceeded, etc.), it returns an error with the appropriate HTTP status code:

{ "error": { "code": "missing_required_field", "message": "The required field 'value' is missing from the request body.", "status": 400 } }
FieldTypeDescription
error.codestringMachine-readable error identifier
error.messagestringHuman-readable description
error.statusintegerHTTP status code

HTTP status codes

StatusMeaning
200 OKValidation processed successfully — check the response body for the result
400 Bad RequestMissing or malformed input (e.g., missing value field)
401 UnauthorizedMissing or invalid API key
403 ForbiddenYour plan does not have access to this feature
429 Too Many RequestsRate limit exceeded — e.g., FREE plan quota exceeded or too many concurrent / in-flight requests
500 Internal Server ErrorVerifNow server error (rare)
503 Service UnavailableTemporary outage — retry after a moment

Error codes reference

Authentication errors

CodeStatusDescription
missing_api_key401No X-API-KEY header was provided
invalid_api_key401The API key is malformed or does not exist
revoked_api_key401The API key has been revoked
insufficient_permissions403This key lacks access to the endpoint

Request errors

CodeStatusDescription
missing_required_field400The required value field is missing from the request body
invalid_request400The request body is malformed or cannot be parsed

Quota errors

CodeStatusDescription
quota_exceeded429You have used your quota for this billing period

FREE plan: requests are blocked when the quota is reached. Paid plans (STARTER, GROWTH, PRO): requests above the quota are allowed but billed per unit at the end of the billing period.


Handling responses in code

JavaScript / TypeScript

interface ValidationResult { valid: boolean message: string normalizedValue: string originalValue: string validationLevel: string emailDetails: { signals: Record<string, unknown> risk_score: number risk_level: string deliverability: 'DELIVERABLE' | 'RISKY' | 'UNDELIVERABLE' | 'UNKNOWN' applied_level: string } } interface VerifNowError { error: { code: string message: string status: number } } async function validateEmail(email: string) { const response = await fetch('https://api.verifnow.io/api/v1/validate/email', { method: 'POST', headers: { 'X-API-KEY': process.env.VERIFNOW_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ value: email }), }) const data = await response.json() // Handle API errors (4xx / 5xx) if (!response.ok) { const err = data as VerifNowError switch (err.error.code) { case 'missing_api_key': case 'invalid_api_key': throw new Error('Invalid API key. Check your VERIFNOW_API_KEY environment variable.') case 'quota_exceeded': throw new Error('Quota exceeded. Upgrade your plan at app.verifnow.io') default: throw new Error(`VerifNow error: ${err.error.message}`) } } // Validation processed — check the result const result = data as ValidationResult switch (result.emailDetails.deliverability) { case 'DELIVERABLE': console.log('✅ Email is deliverable') break case 'RISKY': console.warn('⚠️ Email is valid but has risk factors') break case 'UNDELIVERABLE': console.error('❌ Email is unlikely to be deliverable') break case 'UNKNOWN': console.warn('❓ Unable to fully verify deliverability') break } return result }

Retry with exponential backoff

For 429 and 5xx errors, implement retry logic with backoff:

async function validateEmailWithRetry( email: string, maxRetries = 3 ): Promise<unknown> { for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await fetch('https://api.verifnow.io/api/v1/validate/email', { method: 'POST', headers: { 'X-API-KEY': process.env.VERIFNOW_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ value: email }), }) if (response.status === 429 || response.status >= 500) { const retryAfter = response.headers.get('Retry-After') const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000 // exponential backoff if (attempt < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, delay)) continue } } return await response.json() } catch (err) { if (attempt === maxRetries - 1) throw err } } }

Best practices

  1. A 200 does not mean the email is valid — always check valid and deliverability in the response body
  2. Only non-200 responses indicate API errors — use response.ok or the status code to distinguish
  3. Log error codes (not just messages) for debugging — codes are stable across versions
  4. Implement retry logic for 429 and 5xx errors
  5. Surface suggested_domain from emailDetails.signals to users when a typo is detected
  6. Monitor your quota in the dashboard  to avoid hitting limits
Last updated on