Verify Endpoint
Validate a single email address and get instant results. Perfect for signup forms, CRM integrations, or any real-time validation needs.
Endpoint
POST https://api.mailcop.net/v1/verify
Authentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Request
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | Yes | The email address to validate |
validation_type |
string | No | mx or smtp. Defaults to mx |
catch_all |
boolean | No | SMTP only. Also check whether the address’s domain accepts mail for any mailbox, returned as catch_all_domain. No extra credits. Defaults to false |
Example request
curl -X POST https://api.mailcop.net/v1/verify \
-H "Authorization: Bearer tm_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"validation_type": "mx"
}'
Response
Success (200 OK)
{
"email": "[email protected]",
"status": "good",
"validation_type": "mx",
"role_account": false,
"free_provider": false,
"catch_all_domain": null,
"result": {
"domain": "example.com",
"checks": { "filter": "pass", "disposable": "pass", "format": "pass", "mx": "pass", "smtp": "skipped" },
"smtp": null
},
"credits_used": 1
}
Bad SMTP result
{
"email": "[email protected]",
"status": "bad",
"validation_type": "smtp",
"reason": "Mailbox does not exist",
"reason_code": "mailbox_not_found",
"role_account": false,
"free_provider": false,
"catch_all_domain": null,
"result": {
"domain": "club.org",
"checks": { "filter": "pass", "disposable": "pass", "format": "pass", "mx": "pass", "smtp": "fail" },
"smtp": { "code": 550, "enhanced_code": "5.1.1" }
},
"credits_used": 10
}
Response fields
| Field | Type | Description |
|---|---|---|
email |
string | The email address that was validated |
status |
string | Result: good or bad |
validation_type |
string | Method used: mx or smtp |
credits_used |
integer | Number of credits charged |
reason |
string | Human-readable reason (only for bad status) |
reason_code |
string | Stable machine-readable reason, see below (only for bad status) |
result.domain |
string | Domain part of the address, null if the format is invalid |
result.checks |
object | pass, fail or skipped for each stage, in order: filter, disposable, format, mx, smtp. Stages after a failure are skipped |
role_account |
boolean | Local part is a role mailbox such as info@, support@, sales@ |
free_provider |
boolean | Domain is a free mailbox provider such as Gmail or Yahoo |
catch_all_domain |
boolean | About the domain, not this address. true if the domain accepts mail for any mailbox, so a good SMTP result cannot confirm this mailbox exists: every address at the domain returns the same. false if the domain rejected a random mailbox, so good really means this mailbox exists. null when not requested, not a good SMTP result, or when the check was inconclusive (for example greylisted) |
result.smtp |
object | When the SMTP check failed with a server reply: code (e.g. 550) and enhanced_code (e.g. "5.1.1", null if the server sent none). null otherwise. The reply text is never returned |
result.disposable |
boolean | true when rejected as a disposable address |
Status values
| Status | Meaning |
|---|---|
good |
Valid email address, safe to send to |
bad |
Invalid email, will likely bounce |
Failure reasons
When status is bad, reason_code tells you why. Branch on reason_code; reason is display text and may change.
reason_code |
Description |
|---|---|
invalid_format |
Email format is incorrect |
no_mx_records |
Domain doesn’t exist or has no mail servers. A DNS timeout also lands here, so retry before treating it as permanent |
mailbox_not_found |
Specific mailbox doesn’t exist (SMTP only). Only reported for mailbox-unknown replies, never for policy or reputation blocks |
mailbox_full |
Recipient’s mailbox is full (SMTP only) |
smtp_rejected |
Mail server refused the check, e.g. greylisting or a policy block (SMTP only). Says nothing about whether the mailbox exists |
smtp_unreachable |
Could not connect to the mail server (SMTP only) |
disposable_domain |
Known temporary email service |
blocked_by_filter |
Matched one of your blocklist filters. reason holds your filter’s reason when set |
Validation Types
MX Validation (default)
- Cost: 1 credit
- Speed: ~100ms
- Checks: Email format, domain existence, MX records
{
"email": "[email protected]",
"validation_type": "mx"
}
SMTP Validation
- Cost: 10 credits
- Speed: ~1-3 seconds
- Checks: Everything in MX, plus mailbox existence
- Requires: Premium plan
{
"email": "[email protected]",
"validation_type": "smtp"
}
Error Responses
Missing email parameter (400)
{
"error": "Email parameter is required"
}
Invalid validation type (422)
{
"error": "Invalid validation_type. Must be 'mx' or 'smtp'",
"valid_types": ["mx", "smtp"]
}
SMTP requires Premium (402)
{
"error": "SMTP validation requires a Premium subscription",
"current_plan": "starter"
}
Insufficient credits (402)
{
"error": "Insufficient account credits"
}
Invalid API key (401)
{
"error": "Invalid API key"
}
IP not allowed (403)
{
"error": "IP address not allowed"
}
Rate limit exceeded (429)
{
"error": "Rate limit exceeded"
}
Code Examples
JavaScript - Form validation
async function validateEmail(email) {
const response = await fetch('https://api.mailcop.net/v1/verify', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
return response.json();
}
// Usage in a form
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.querySelector('#email').value;
try {
const result = await validateEmail(email);
if (result.status === 'good') {
// Proceed with form submission
e.target.submit();
} else {
alert('Please enter a valid email address');
}
} catch (error) {
console.error('Validation error:', error);
}
});
Python - Batch validation
import requests
import time
API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://api.mailcop.net/v1/verify'
def validate_email(email, validation_type='mx'):
response = requests.post(
BASE_URL,
headers={'Authorization': f'Bearer {API_KEY}'},
json={'email': email, 'validation_type': validation_type}
)
if response.status_code == 429:
# Rate limited, wait and retry
time.sleep(5)
return validate_email(email, validation_type)
return response.json()
# Validate a list of emails
emails = ['[email protected]', '[email protected]', '[email protected]']
results = []
for email in emails:
result = validate_email(email)
results.append(result)
print(f"{email}: {result['status']}")
Ruby - Rails integration
class EmailValidationService
API_URL = 'https://api.mailcop.net/v1/verify'
def initialize(api_key)
@api_key = api_key
end
def validate(email, validation_type: 'mx')
response = HTTP.auth("Bearer #{@api_key}")
.post(API_URL, json: {
email: email,
validation_type: validation_type
})
JSON.parse(response.body)
end
end
# Usage
validator = EmailValidationService.new(ENV['TRUEMAIL_API_KEY'])
result = validator.validate('[email protected]')
if result['status'] == 'good'
# Email is valid
end
PHP - WordPress plugin example
function truemail_validate_email($email) {
$api_key = get_option('truemail_api_key');
$response = wp_remote_post('https://api.mailcop.net/v1/verify', [
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json'
],
'body' => json_encode(['email' => $email])
]);
if (is_wp_error($response)) {
return null;
}
return json_decode(wp_remote_retrieve_body($response), true);
}
// Hook into registration
add_filter('registration_errors', function($errors, $sanitized_user_login, $user_email) {
$result = truemail_validate_email($user_email);
if ($result && $result['status'] === 'bad') {
$errors->add('invalid_email', 'Please enter a valid email address.');
}
return $errors;
}, 10, 3);
Best Practices
1. Use appropriate validation type
- MX validation for high-volume, speed-critical scenarios
- SMTP validation for important signups or campaigns
2. Handle errors gracefully
Always check for error responses and handle rate limits with retries.
3. Cache results
If validating the same email multiple times, cache the result to save credits.
4. Validate server-side
Never expose your API key in client-side code. Always validate from your backend.
5. Combine with filters
Use filters to block known bad addresses before they use credits.
Related
- API Overview - Authentication and basics
- Validation Types - MX vs SMTP explained
- Error Handling - Complete error reference