Skip to main content

Rate Limits & Error Handling

Every API has limits to ensure fair usage and system stability. This guide explains our rate limits, error responses, and how to handle them gracefully in your code.


Rate Limits

We enforce rate limits to protect our infrastructure and ensure consistent performance for all users.

Current Limits

EndpointPer-Minute LimitDaily Limit
/verify-single60 requests10,000 requests
/verify-bulk60 requests10,000 requests
/get-results/{task_id}120 requests10,000 requests
info

These limits apply to the number of API requests, not the number of emails verified. A bulk verification task with 1,000 emails counts as 1 request.

How Rate Limits Work

Per-minute limit: You can make up to 60 requests (or 120 for get-results) within any 60-second window. The counter resets on a rolling basis.

Daily limit: You can make up to 10,000 requests per 24-hour period, resetting at midnight UTC.

Rate Limit Headers

Every successful API response includes headers showing your current usage:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-DailyLimit-Remaining: 9500
HeaderDescription
X-RateLimit-LimitMaximum requests allowed per minute
X-RateLimit-RemainingRequests remaining in current minute window
X-DailyLimit-RemainingRequests remaining for the day

Rate Limit Exceeded Response

When you exceed the per-minute rate limit:

HTTP Status: 429 Too Many Requests

{
"error": "Rate limit exceeded",
"limit": 60,
"window": "1 minute",
"current": 61,
"retry_after_seconds": 45
}

Headers included:

Retry-After: 45
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704312045
HeaderDescription
Retry-AfterSeconds to wait before retrying
X-RateLimit-ResetUnix timestamp when the limit resets

Daily Limit Exceeded Response

When you exceed the daily limit:

HTTP Status: 429 Too Many Requests

{
"error": "Daily limit exceeded",
"limit": 10000,
"current": 10001,
"resets_at": "midnight UTC"
}

Headers included:

X-DailyLimit-Limit: 10000
X-DailyLimit-Remaining: 0
warning

Repeatedly hitting rate limits may trigger additional protection measures, including temporary IP blocking.


Error Responses by Endpoint

Single Verification Errors (/verify-single)

HTTP StatusErrorDescription
400Email is requiredMissing email in request body
401Missing or invalid Authorization headerNo Bearer token provided
401Invalid or inactive API keyAPI key doesn't exist or is inactive
402Insufficient creditsNot enough credits to verify
403API key is suspendedKey has been suspended
403Access deniedIP address is blocked
429Rate limit exceededToo many requests per minute
429Daily limit exceededToo many requests today
500Internal server errorSomething went wrong on our end

Bulk Verification Errors (/verify-bulk)

HTTP StatusErrorDescription
400Emails array is required and must not be emptyMissing or empty emails array
400Maximum 1,000,000 emails allowed per requestToo many emails in one request
401Missing or invalid Authorization headerNo Bearer token provided
401Invalid or inactive API keyAPI key doesn't exist or is inactive
402Insufficient creditsNot enough credits for all emails
403API key is suspendedKey has been suspended
403Access deniedIP address is blocked
429Rate limit exceededToo many requests per minute
429Daily limit exceededToo many requests today
500Internal server errorSomething went wrong on our end

Get Results Errors (/get-results/{task_id})

HTTP StatusErrorDescription
400Task ID is requiredMissing task_id in URL
401Missing or invalid Authorization headerNo Bearer token provided
401Invalid or inactive API keyAPI key doesn't exist or is inactive
403Unauthorized to access this taskTask belongs to another user
403API key is suspendedKey has been suspended
403Access deniedIP address is blocked
404Task not foundTask ID doesn't exist
429Rate limit exceededToo many requests per minute
429Daily limit exceededToo many requests today
500Internal server errorSomething went wrong on our end

Error Response Examples

Missing Authorization Header (401)

{
"error": "Missing or invalid Authorization header. Use: Authorization: Bearer VEC..."
}

Invalid API Key (401)

{
"error": "Invalid or inactive API key"
}
warning

Invalid API key attempts are logged. Repeated failures from the same IP address may result in automatic IP blocking.

Suspended API Key (403)

{
"error": "API key is suspended",
"reason": "This API key has been suspended"
}

IP Blocked (403)

{
"error": "Access denied",
"reason": "IP address is blocked",
"blocked_until": "2024-12-31T23:59:59Z"
}

Insufficient Credits - Single (402)

{
"error": "Insufficient credits",
"current_balance": 0,
"message": "Please purchase more credits to continue"
}

Insufficient Credits - Bulk (402)

{
"error": "Insufficient credits",
"required": 5000,
"current_balance": 1000,
"message": "You need 5000 credits but only have 1000 available"
}

Missing Email Field (400)

{
"error": "Email is required"
}

Task Not Found (404)

{
"error": "Task not found"
}

IP Blocking

Our system automatically monitors for suspicious activity and blocks IPs that exhibit malicious behavior.

What Triggers IP Blocking

  • Repeated invalid API key attempts
  • Excessive failed authentication
  • Attempting to bypass rate limits
  • Unusual request patterns

When Blocked

You'll receive a 403 response:

{
"error": "Access denied",
"reason": "IP address is blocked",
"blocked_until": "2024-12-31T23:59:59Z"
}

The blocked_until field shows when the block expires. Some blocks may be permanent (shown as "permanent").

If You're Blocked

  1. Wait - Temporary blocks lift automatically at the specified time
  2. Check your integration - Look for bugs causing excessive requests or auth failures
  3. Contact support - If you believe it's a mistake: support@validemailchecker.com
danger

Attempting to bypass security measures (e.g., rotating IPs to avoid blocks) will result in permanent blocking.


Credit Usage & Refunds

How Credits Work

  • Single verification: 1 credit per email
  • Bulk verification: 1 credit per email in the task
  • Credits are deducted when verification is processed

Credit Refunds

Credits are automatically refunded for emails that return an unknown status. This happens when:

  • The email server was temporarily unavailable
  • Anti-spam systems blocked our verification
  • A timeout occurred during verification
tip

You're never charged for verifications that couldn't be completed. Check for unknown status results and retry them later for a definitive answer.

When Credits Are NOT Deducted

  • 4xx errors (bad request, auth errors, rate limits)
  • 5xx errors (server errors)
  • unknown verification results (refunded)

Handling Errors in Code

JavaScript/Node.js

const API_KEY = process.env.VEC_API_KEY;
const BASE_URL = 'https://app.validemailchecker.com/api';

async function verifyEmail(email) {
const response = await fetch(`${BASE_URL}/verify-single`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});

const data = await response.json();

// Handle specific error cases
switch (response.status) {
case 200:
return data; // Success!

case 400:
throw new Error(`Bad request: ${data.error}`);

case 401:
throw new Error('Invalid API key - check your credentials');

case 402:
throw new Error(`Insufficient credits. Balance: ${data.current_balance}`);

case 403:
if (data.error.includes('suspended')) {
throw new Error('API key suspended - check dashboard');
}
if (data.error.includes('blocked')) {
throw new Error(`IP blocked until: ${data.blocked_until}`);
}
throw new Error(`Access denied: ${data.error}`);

case 429:
const retryAfter = data.retry_after_seconds || 60;
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
throw new Error(`Rate limited - retry after ${retryAfter}s`);

case 500:
throw new Error('Server error - please retry');

default:
throw new Error(`Unexpected error: ${data.error}`);
}
}

With Exponential Backoff

async function verifyWithRetry(email, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await verifyEmail(email);
} catch (error) {
// Only retry on rate limits or server errors
if (error.message.includes('Rate limited')) {
const waitTime = Math.pow(2, attempt) * 30 * 1000; // 30s, 60s, 120s
console.log(`Attempt ${attempt + 1} failed. Waiting ${waitTime/1000}s...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}

if (error.message.includes('Server error') && attempt < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 5000));
continue;
}

throw error; // Don't retry auth or credit errors
}
}
throw new Error('Max retries exceeded');
}

Python

import requests
import time

API_KEY = os.environ.get('VEC_API_KEY')
BASE_URL = 'https://app.validemailchecker.com/api'

def verify_email(email):
response = requests.post(
f'{BASE_URL}/verify-single',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
},
json={'email': email}
)

data = response.json()

if response.status_code == 200:
return data

if response.status_code == 401:
raise Exception('Invalid API key')

if response.status_code == 402:
raise Exception(f"Insufficient credits: {data.get('current_balance', 0)} remaining")

if response.status_code == 403:
raise Exception(f"Access denied: {data.get('error')}")

if response.status_code == 429:
retry_after = data.get('retry_after_seconds', 60)
raise Exception(f"Rate limited. Retry after {retry_after} seconds")

raise Exception(f"API error: {data.get('error')}")

def verify_with_retry(email, max_retries=3):
for attempt in range(max_retries):
try:
return verify_email(email)
except Exception as e:
if 'Rate limited' in str(e):
wait_time = (2 ** attempt) * 30
print(f'Rate limited. Waiting {wait_time}s...')
time.sleep(wait_time)
continue
raise
raise Exception('Max retries exceeded')

Monitoring Rate Limit Headers

Check headers proactively to avoid hitting limits:

async function makeRequest(endpoint, options) {
const response = await fetch(`${BASE_URL}${endpoint}`, options);

// Check remaining requests
const remaining = response.headers.get('X-RateLimit-Remaining');
const dailyRemaining = response.headers.get('X-DailyLimit-Remaining');

if (remaining && parseInt(remaining) < 10) {
console.warn(`⚠️ Only ${remaining} requests remaining this minute`);
}

if (dailyRemaining && parseInt(dailyRemaining) < 100) {
console.warn(`⚠️ Only ${dailyRemaining} requests remaining today`);
}

return response;
}

Best Practices

Do ✅

  • Check X-RateLimit-Remaining headers before making requests
  • Implement exponential backoff for rate limit errors
  • Handle all HTTP status codes appropriately
  • Log errors for debugging
  • Use the Retry-After header value when rate limited

Don't ❌

  • Ignore rate limit responses and keep retrying immediately
  • Retry authentication errors (they won't succeed)
  • Share API keys between multiple servers (use separate keys)
  • Ignore the blocked_until field when IP blocked

Common Questions

What counts as a "request"?

Each API call counts as one request, regardless of whether it succeeds or fails, and regardless of how many emails are in a bulk task.

Do failed requests count toward limits?

Yes. All requests count toward your rate limits, even if they fail.

Can I get higher rate limits?

Contact support@validemailchecker.com if you have a legitimate need for higher limits. We evaluate requests case-by-case.

Why was my IP blocked?

IP blocking is triggered by repeated authentication failures or suspicious activity. Check your integration for bugs, especially around API key handling.

Do I get credits back for unknown results?

Yes! Credits are automatically refunded for emails that return unknown status since we couldn't complete the verification.


Next Steps

-> Code Examples - Complete implementations with error handling

-> Single Verification Endpoint - Endpoint details

-> Bulk Verification Endpoint - Bulk verification workflow