POST /validate
curl --request POST \
--url https://api.vouch.expert/validate \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--header 'X-Project-Id: <x-project-id>' \
--data '
{
"email": "<string>",
"fingerprintHash": "<string>",
"sdkVersion": "<string>",
"ip": "<string>",
"userAgent": "<string>",
"validations": {}
}
'import requests
url = "https://api.vouch.expert/validate"
payload = {
"email": "<string>",
"fingerprintHash": "<string>",
"sdkVersion": "<string>",
"ip": "<string>",
"userAgent": "<string>",
"validations": {}
}
headers = {
"Authorization": "<authorization>",
"X-Project-Id": "<x-project-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: '<authorization>',
'X-Project-Id': '<x-project-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: '<string>',
fingerprintHash: '<string>',
sdkVersion: '<string>',
ip: '<string>',
userAgent: '<string>',
validations: {}
})
};
fetch('https://api.vouch.expert/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vouch.expert/validate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'fingerprintHash' => '<string>',
'sdkVersion' => '<string>',
'ip' => '<string>',
'userAgent' => '<string>',
'validations' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json",
"X-Project-Id: <x-project-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vouch.expert/validate"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("X-Project-Id", "<x-project-id>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vouch.expert/validate")
.header("Authorization", "<authorization>")
.header("X-Project-Id", "<x-project-id>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vouch.expert/validate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["X-Project-Id"] = '<x-project-id>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}"
response = http.request(request)
puts response.read_body{
"error": "Invalid email format"
}
{
"error": "Invalid API key"
}
{
"error": "Monthly validation quota exceeded"
}
{
"error": "Too many requests"
}
{
"error": "An internal error occurred"
}
Endpoints
POST /validate
Validate an email address with device fingerprinting and fraud detection
POST
/
validate
POST /validate
curl --request POST \
--url https://api.vouch.expert/validate \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--header 'X-Project-Id: <x-project-id>' \
--data '
{
"email": "<string>",
"fingerprintHash": "<string>",
"sdkVersion": "<string>",
"ip": "<string>",
"userAgent": "<string>",
"validations": {}
}
'import requests
url = "https://api.vouch.expert/validate"
payload = {
"email": "<string>",
"fingerprintHash": "<string>",
"sdkVersion": "<string>",
"ip": "<string>",
"userAgent": "<string>",
"validations": {}
}
headers = {
"Authorization": "<authorization>",
"X-Project-Id": "<x-project-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: '<authorization>',
'X-Project-Id': '<x-project-id>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: '<string>',
fingerprintHash: '<string>',
sdkVersion: '<string>',
ip: '<string>',
userAgent: '<string>',
validations: {}
})
};
fetch('https://api.vouch.expert/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vouch.expert/validate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => '<string>',
'fingerprintHash' => '<string>',
'sdkVersion' => '<string>',
'ip' => '<string>',
'userAgent' => '<string>',
'validations' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json",
"X-Project-Id: <x-project-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vouch.expert/validate"
payload := strings.NewReader("{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("X-Project-Id", "<x-project-id>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vouch.expert/validate")
.header("Authorization", "<authorization>")
.header("X-Project-Id", "<x-project-id>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vouch.expert/validate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["X-Project-Id"] = '<x-project-id>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"<string>\",\n \"fingerprintHash\": \"<string>\",\n \"sdkVersion\": \"<string>\",\n \"ip\": \"<string>\",\n \"userAgent\": \"<string>\",\n \"validations\": {}\n}"
response = http.request(request)
puts response.read_body{
"error": "Invalid email format"
}
{
"error": "Invalid API key"
}
{
"error": "Monthly validation quota exceeded"
}
{
"error": "Too many requests"
}
{
"error": "An internal error occurred"
}
Overview
The/validate endpoint is the core of Vouch’s API. It performs comprehensive email validation including syntax checking, disposable email detection, MX verification, and device fingerprinting.
You can use either the versioned endpoint
/v1/validate or the latest endpoint /validateAuthentication
string
required
Bearer token with your API key
Authorization: Bearer your_api_key_here
string
required
Your project ID
X-Project-Id: your_project_id
Request Body
string
required
The email address to validate (will be normalized to lowercase)
string
Device fingerprint hash for client-side validation and device tracking.
"abc123def456789..."
string
SDK version identifier (automatically included by SDKs)
"@vouch-in/[email protected]"
string
Server-side only: Override client IP address for validation. If not provided, the API will use the request’s IP address from headers (CF-Connecting-IP or X-Forwarded-For).Only available when using a server API key.
"192.168.1.1"
string
Server-side only: Override User-Agent for validation. If not provided, the API will use the request’s User-Agent header.Only available when using a server API key.
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
object
Optional override for which validations to run and their actions (allow/flag/block)
{
"syntax": "block",
"disposable": "block",
"mx": "block",
"roleEmail": "flag",
"alias": "flag",
"device": "flag",
"ip": "flag"
}
The SDKs automatically construct the request body. When using the API directly, ensure all required fields are included.
Response
object
required
Individual validation check results. Each check contains:
Available checks include:
Show CheckResult Structure
Show CheckResult Structure
syntax, disposable, mx, roleEmail, alias, device, ipobject
required
string | null
required
Human-friendly error message when the recommendation is
flag or block (e.g. “Please use a permanent email address, not a temporary one.”). null when the recommendation is allow.string
required
Overall recommendation based on all checks:
allow, flag, or blockarray
required
Array of signal identifiers detected during validation (e.g.,
["disposable_email", "vpn_detected"])Example Request
curl --request POST \
--url https://api.vouch.expert/validate \
--header 'Authorization: Bearer your_server_api_key' \
--header 'Content-Type: application/json' \
--header 'X-Project-Id: your_project_id' \
--data '{
"email": "[email protected]",
"ip": "192.168.1.1",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}'
const response = await fetch('https://api.vouch.expert/validate', {
method: 'POST',
headers: {
'Authorization': 'Bearer your_server_api_key',
'X-Project-Id': 'your_project_id',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: '[email protected]',
ip: '192.168.1.1',
userAgent: 'Mozilla/5.0...'
})
});
const result = await response.json();
import requests
response = requests.post(
'https://api.vouch.expert/validate',
headers={
'Authorization': 'Bearer your_server_api_key',
'X-Project-Id': 'your_project_id',
'Content-Type': 'application/json'
},
json={
'email': '[email protected]',
'ip': '192.168.1.1',
'userAgent': 'Mozilla/5.0...'
}
)
result = response.json()
package main
import (
"bytes"
"encoding/json"
"net/http"
)
func main() {
payload := map[string]interface{}{
"email": "[email protected]",
"ip": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.vouch.expert/validate", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer your_server_api_key")
req.Header.Set("X-Project-Id", "your_project_id")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Client-side requests: Use the JavaScript/React SDKs which automatically include device fingerprinting in the
fingerprint field.Server-side requests: Only provide ip and userAgent if you need to override the request’s headers.Example Response
{
"checks": {
"syntax": {
"pass": true,
"latency": 0.8
},
"disposable": {
"pass": true,
"latency": 12.3
},
"mx": {
"pass": true,
"latency": 45.2
},
"roleEmail": {
"pass": true,
"latency": 0.5
},
"alias": {
"pass": true,
"latency": 0.3
},
"device": {
"pass": true,
"latency": 15.7
},
"ip": {
"pass": true,
"latency": 8.4
}
},
"message": null,
"metadata": {
"fingerprintHash": "abc123def456789",
"previousSignups": 0,
"totalLatency": 83.2
},
"recommendation": "allow",
"signals": []
}
Example with Signals
{
"checks": {
"syntax": {
"pass": true,
"latency": 0.8
},
"disposable": {
"pass": false,
"latency": 12.3
},
"mx": {
"pass": true,
"latency": 45.2
},
"device": {
"pass": false,
"latency": 15.7
},
"ip": {
"pass": false,
"latency": 8.4
}
},
"message": "Please use a permanent email address, not a temporary one.",
"metadata": {
"fingerprintHash": "abc123def456789",
"previousSignups": 3,
"totalLatency": 82.4
},
"recommendation": "block",
"signals": [
"disposable_email",
"device_reuse",
"device_seen_3_times",
"vpn_detected"
]
}
Error Responses
All errors return an error object with HTTP status codes.{
"error": "Invalid email format"
}
{
"error": "Invalid API key"
}
{
"error": "Monthly validation quota exceeded"
}
{
"error": "Too many requests"
}
{
"error": "An internal error occurred"
}
Rate Limits
| Key Type | Rate Limit | Window |
|---|---|---|
| Client Key | 1,000 requests | Per hour |
| Server Key | 5,000 requests | Per hour |
Rate limits are per project. If you exceed the limit, you’ll receive a 429 error with a
retryAfter value in seconds.Quota Management
Each plan includes a monthly validation quota. Track your usage in the dashboard or via the response headers:X-Quota-Limit: 10000
X-Quota-Used: 2345
X-Quota-Remaining: 7655
X-Quota-Reset: 2024-01-01T00:00:00Z
Best Practices
Use the Right API Key
Use the Right API Key
- Client keys for browser/mobile apps (includes automatic fingerprinting)
- Server keys for backend validation (can override IP/User-Agent)
- Never expose server keys in client-side code
Handle Recommendations Appropriately
Handle Recommendations Appropriately
- block: Reject the signup immediately
- flag: Allow signup but mark for review, or require additional verification
- allow: Proceed normally
Cache Results When Appropriate
Cache Results When Appropriate
- Cache validation results for repeated validations of the same email
- Set TTL based on your use case (e.g., 24 hours)
- Invalidate cache if user changes email
Provide Good User Experience
Provide Good User Experience
- Option 1: Use the
messagefield to display user-friendly error messages directly - Option 2: Check individual
checksfor custom error messages and business logic - Don’t expose internal validation details (signals, metadata) to users
- Consider allowing flagged emails with additional verification
Monitor Your Quota
Monitor Your Quota
- Set up alerts when approaching quota limits
- Upgrade plan before hitting limits
Next Steps
Validation Types
Learn about each validation check
Error Handling
Handle errors gracefully
Use SDKs
Use our SDKs for easier integration
Best Practices
Production optimization tips