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
| Endpoint | Per-Minute Limit | Daily Limit |
|---|---|---|
/verify-single | 60 requests | 10,000 requests |
/verify-bulk | 60 requests | 10,000 requests |
/get-results/{task_id} | 120 requests | 10,000 requests |
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
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per minute |
X-RateLimit-Remaining | Requests remaining in current minute window |
X-DailyLimit-Remaining | Requests 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
| Header | Description |
|---|---|
Retry-After | Seconds to wait before retrying |
X-RateLimit-Reset | Unix 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
Repeatedly hitting rate limits may trigger additional protection measures, including temporary IP blocking.
Error Responses by Endpoint
Single Verification Errors (/verify-single)
| HTTP Status | Error | Description |
|---|---|---|
| 400 | Email is required | Missing email in request body |
| 401 | Missing or invalid Authorization header | No Bearer token provided |
| 401 | Invalid or inactive API key | API key doesn't exist or is inactive |
| 402 | Insufficient credits | Not enough credits to verify |
| 403 | API key is suspended | Key has been suspended |
| 403 | Access denied | IP address is blocked |
| 429 | Rate limit exceeded | Too many requests per minute |
| 429 | Daily limit exceeded | Too many requests today |
| 500 | Internal server error | Something went wrong on our end |
Bulk Verification Errors (/verify-bulk)
| HTTP Status | Error | Description |
|---|---|---|
| 400 | Emails array is required and must not be empty | Missing or empty emails array |
| 400 | Maximum 1,000,000 emails allowed per request | Too many emails in one request |
| 401 | Missing or invalid Authorization header | No Bearer token provided |
| 401 | Invalid or inactive API key | API key doesn't exist or is inactive |
| 402 | Insufficient credits | Not enough credits for all emails |
| 403 | API key is suspended | Key has been suspended |
| 403 | Access denied | IP address is blocked |
| 429 | Rate limit exceeded | Too many requests per minute |
| 429 | Daily limit exceeded | Too many requests today |
| 500 | Internal server error | Something went wrong on our end |
Get Results Errors (/get-results/{task_id})
| HTTP Status | Error | Description |
|---|---|---|
| 400 | Task ID is required | Missing task_id in URL |
| 401 | Missing or invalid Authorization header | No Bearer token provided |
| 401 | Invalid or inactive API key | API key doesn't exist or is inactive |
| 403 | Unauthorized to access this task | Task belongs to another user |
| 403 | API key is suspended | Key has been suspended |
| 403 | Access denied | IP address is blocked |
| 404 | Task not found | Task ID doesn't exist |
| 429 | Rate limit exceeded | Too many requests per minute |
| 429 | Daily limit exceeded | Too many requests today |
| 500 | Internal server error | Something 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"
}
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
- Wait - Temporary blocks lift automatically at the specified time
- Check your integration - Look for bugs causing excessive requests or auth failures
- Contact support - If you believe it's a mistake: support@validemailchecker.com
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
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)
unknownverification 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-Remainingheaders before making requests - Implement exponential backoff for rate limit errors
- Handle all HTTP status codes appropriately
- Log errors for debugging
- Use the
Retry-Afterheader 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_untilfield 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