API Reference
Complete BillionVerify API reference for authentication, email verification, file jobs, downloads, credits, and webhooks.
The BillionVerify API is a RESTful service for real-time email verification, synchronous batch checks, asynchronous file processing, credits, and webhooks. All requests must be sent over HTTPS.
Base URL
https://api.billionverify.com/v1OpenAPI Specification
The canonical machine-readable contract is available here:
- OpenAPI YAML: https://api.billionverify.com/openapi.yaml
- Interactive API Docs: https://api.billionverify.com/docs
Authentication
Send your API key with one of these headers, in the same priority order used by the server:
BILLIONVERIFY-API-KEYBV-API-KEYEV-API-KEYEMAILVERIFY-API-KEYAuthorization: Bearer <api_key>
Primary header example:
BILLIONVERIFY-API-KEY: bv_live_...Get your API key from the BillionVerify dashboard.
Example
curl -X POST https://api.billionverify.com/v1/verify/single \
-H "BILLIONVERIFY-API-KEY: bv_live_xxx" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","check_smtp":false}'Rate Limits
Rate limits are applied per API key and endpoint type.
| Endpoint | Rate Limit |
|---|---|
Single Verification (/verify/single) | 6,000 requests/minute |
Batch Verification (/verify/bulk) | 1,500 requests/minute |
File Upload (/verify/file) | 300 requests/minute |
Other endpoints (/credits, /webhooks, etc.) | 200 requests/minute |
When rate limited, the API returns 429 Too Many Requests:
{
"success": false,
"code": "4290",
"message": "Rate limit exceeded",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later."
}
}Response Format
Successful responses:
{
"success": true,
"code": "0",
"message": "Success",
"data": {}
}Error responses:
{
"success": false,
"code": "4010",
"message": "Unauthorized",
"error": {
"code": "INVALID_API_KEY",
"message": "API key is invalid or missing"
}
}Endpoints
Single Email Verification
Verify a single email synchronously.
POST /verify/singleRequest
{
"email": "user@example.com",
"check_smtp": false
}Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | Yes | - | Email address to verify |
check_smtp | boolean | No | false | Perform live SMTP mailbox verification |
Notes
check_smtpdefaults tofalsewhen omitted.- When
check_smtp=false, the request skips live SMTP network checks.
Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"email": "user@example.com",
"status": "valid",
"score": 0.95,
"is_deliverable": true,
"is_disposable": false,
"is_catchall": false,
"is_role": false,
"is_free": false,
"has_gravatar": false,
"gravatar_url": "",
"domain": "example.com",
"domain_age": 10,
"mx_records": ["mail.example.com"],
"domain_reputation": {
"mx_ip": "192.0.2.1",
"is_listed": false,
"blacklists": [],
"checked": true
},
"smtp_check": false,
"reason": "accepted",
"smtp_response": "",
"error_message": "",
"domain_suggestion": "",
"response_time": 250,
"credits_used": 1
}
}Batch Email Verification
Verify multiple email addresses synchronously. Use this for small batches of up to 100 emails.
POST /verify/bulkRequest
{
"emails": ["user1@example.com", "user2@example.com"],
"check_smtp": false
}Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
emails | array | Yes | - | Array of email addresses (max: 100) |
check_smtp | boolean | No | false | Perform live SMTP mailbox verification |
Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"results": [
{
"email": "user1@example.com",
"status": "valid",
"score": 0.95,
"is_deliverable": true,
"credits_used": 1
},
{
"email": "user2@example.com",
"status": "invalid",
"score": 0,
"is_deliverable": false,
"credits_used": 0
}
],
"total_emails": 2,
"valid_emails": 1,
"invalid_emails": 1,
"credits_used": 1,
"process_time": 1500
}
}File Upload Verification
Upload a CSV, TXT, XLSX, or XLS file for asynchronous verification.
POST /verify/fileMultipart Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file | file | Yes | - | CSV, TXT, XLSX, or XLS file |
check_smtp | boolean | No | false | Perform live SMTP mailbox verification |
email_column | string | No | auto-detect | Column containing email addresses |
preserve_original | boolean | No | true | Keep original columns in the result file |
Supported Limits
- Maximum file size:
20MB - Maximum emails per file:
100,000
Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"task_id": "7143874e-21c5-43c1-96f3-2b52ea41ae6a",
"file_name": "emails.csv",
"file_size": 45678,
"status": "pending",
"message": "File uploaded successfully, parsing and processing email verification",
"status_url": "https://api.billionverify.com/v1/verify/file/7143874e-21c5-43c1-96f3-2b52ea41ae6a",
"created_at": "2026-02-04T10:30:00Z",
"estimated_count": 1000,
"unique_emails": 950,
"total_rows": 1000,
"email_column": "email"
}
}Get File Job Status
Get the latest status for a file job.
GET /verify/file/{job_id}Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
timeout | integer | No | 0 | Long-polling timeout in seconds (0-300) |
Download Link Semantics
download_urlis the stable API entrypoint for result downloads.direct_download_urlis a signed storage URL for immediate download.direct_download_expires_attells you when that signed storage URL expires.result_file_urlandresult_file_pathare not part of the public API contract.
Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"task_id": "7143874e-21c5-43c1-96f3-2b52ea41ae6a",
"status": "completed",
"progress": 100,
"email_progress": 100,
"chunk_progress": 100,
"progress_source": "email",
"total_emails": 1000,
"processed_emails": 1000,
"valid_emails": 850,
"invalid_emails": 100,
"unknown_emails": 30,
"role_emails": 15,
"catchall_emails": 5,
"risky_emails": 0,
"disposable_emails": 0,
"credits_used": 970,
"download_url": "https://api.billionverify.com/v1/verify/file/7143874e-21c5-43c1-96f3-2b52ea41ae6a/results",
"direct_download_url": "https://download.example.com/results_7143874e.csv?signature=...",
"direct_download_expires_at": "2026-02-04T11:32:05Z",
"started_at": "2026-02-04T10:30:00Z",
"completed_at": "2026-02-04T10:32:05Z",
"total_chunks": 10,
"completed_chunks": 10,
"failed_chunks": 0,
"unique_emails": 950,
"total_rows": 1000
}
}Job Status Values
| Status | Description |
|---|---|
pending | Job is queued |
processing | Job is actively being processed |
uploading | Finalizing or assembling results |
completed | Job finished successfully |
completed_with_warning | Job finished, but with warnings |
failed | Job failed |
Download File Results
Download verification results for a completed file job.
GET /verify/file/{job_id}/resultsBehavior
- Without filters, the endpoint responds with
307 Temporary Redirectto the full result file. - With filters, the endpoint returns a generated CSV directly.
- Filters are combined with OR semantics.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
valid | boolean | Include valid emails |
invalid | boolean | Include invalid emails |
risky | boolean | Include risky emails |
unknown | boolean | Include unknown emails |
catchall | boolean | Include catch-all emails |
role | boolean | Include role emails |
disposable | boolean | Include disposable emails |
Example
curl -L -o filtered-results.csv \
"https://api.billionverify.com/v1/verify/file/{job_id}/results?valid=true&catchall=true&role=true" \
-H "BILLIONVERIFY-API-KEY: bv_live_xxx"Credits
Get the current credit balance for the authenticated account.
GET /creditsResponse
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"account_id": "abc123",
"api_key_id": "key_xyz",
"api_key_name": "Default API Key",
"credits_balance": 9500,
"credits_consumed": 500,
"credits_added": 10000,
"last_updated": "2026-02-04T10:30:00Z"
}
}Webhooks
Use webhooks to receive asynchronous file-job notifications.
Endpoints
POST /webhooksGET /webhooksDELETE /webhooks/{webhook_id}
Supported Events
file.completedfile.failed
verification.completed is not part of the public webhook contract.
Create Webhook
POST /webhooksRequest
{
"url": "https://your-app.com/webhooks/billionverify",
"events": ["file.completed", "file.failed"]
}Rules
urlmust be HTTPSeventsmay contain only supported event names
Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"id": "673f3bff-81ea-4730-9cef-af1eb7f134bf",
"url": "https://your-app.com/webhooks/billionverify",
"events": ["file.completed", "file.failed"],
"secret": "5c609e6893ab3b65ce8940549a030bc165762fe171e0b38f880bd570099619bf",
"is_active": true,
"created_at": "2026-02-04T10:30:00Z",
"updated_at": "2026-02-04T10:30:00Z"
}
}The webhook secret is returned only on creation. Store it securely. You need
it to verify webhook signatures.
List Webhooks
GET /webhooksResponse
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"webhooks": [
{
"id": "673f3bff-81ea-4730-9cef-af1eb7f134bf",
"url": "https://your-app.com/webhooks/billionverify",
"events": ["file.completed", "file.failed"],
"is_active": true,
"last_delivery_status": "success",
"last_delivery_at": "2026-04-01T12:00:00Z",
"last_error": null,
"created_at": "2026-02-04T10:30:00Z",
"updated_at": "2026-02-04T10:30:00Z"
}
],
"total": 1
}
}last_delivery_statusissuccessorfailedwhen a delivery has been attempted.last_delivery_atis the timestamp of the most recent final delivery outcome.last_errorcontains the latest final error message, ornullwhen the latest delivery succeeded.- For webhooks that have never been delivered, all three fields are
null.
Delete Webhook
DELETE /webhooks/{webhook_id}Response
{
"success": true,
"code": "0",
"message": "Success",
"data": {
"message": "Webhook deleted successfully",
"webhook_id": "673f3bff-81ea-4730-9cef-af1eb7f134bf"
}
}Webhook Delivery Headers
| Header | Description |
|---|---|
Content-Type | application/json |
X-Webhook-Event | Event type |
X-Webhook-Timestamp | Timestamp used in signature verification |
X-Webhook-Signature | sha256=<hex_digest> |
Webhook Payload
{
"event": "file.completed",
"timestamp": "2026-02-04T10:35:00Z",
"data": {
"job_id": "7143874e-21c5-43c1-96f3-2b52ea41ae6a",
"file_name": "emails.csv",
"total_emails": 1000,
"valid_emails": 850,
"invalid_emails": 100,
"role_emails": 30,
"catchall_emails": 10,
"unknown_emails": 10,
"disposable_emails": 0,
"credits_used": 990,
"process_time_seconds": 125.5,
"download_url": "https://api.billionverify.com/v1/verify/file/7143874e-21c5-43c1-96f3-2b52ea41ae6a/results",
"direct_download_url": "https://download.example.com/results_7143874e.csv?signature=...",
"direct_download_expires_at": "2026-02-04T11:35:00Z"
}
}Verifying Webhook Signatures
Webhook signatures use HMAC-SHA256 over this exact string:
{timestamp}.{raw_body}The signature header format is:
X-Webhook-Signature: sha256=<hex_digest>Recommended verification flow:
- Read the raw request body exactly as received.
- Read
X-Webhook-Timestamp. - Build
{timestamp}.{raw_body}. - Compute HMAC-SHA256 with your webhook secret.
- Compare against
X-Webhook-Signature. - Reject stale timestamps to reduce replay risk.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, timestamp, signature, secret) {
const signedPayload = `${timestamp}.${rawBody}`;
const expectedSignature =
'sha256=' +
crypto.createHmac('sha256', secret).update(signedPayload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}import hashlib
import hmac
def verify_webhook_signature(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
signed_payload = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(
secret.encode(),
signed_payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(signature, expected)Verification Status
| Status | Meaning | Recommended Action |
|---|---|---|
valid | Email exists and can receive messages | Safe to send |
invalid | Email does not exist or cannot receive | Remove from list |
unknown | Deliverability could not be confirmed | Retry later or use caution |
risky | Potential delivery concern | Use caution and monitor bounces |
disposable | Temporary or disposable mailbox | Usually remove |
catchall | Domain accepts all addresses | Monitor bounce behavior |
role | Role-based mailbox such as info@ or support@ | Usually deliverable, may engage less |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
0 | 200 | Success |
4000 | 400 | Bad request |
4010 | 401 | Invalid or missing API key |
4020 | 402 | Insufficient credits |
4040 | 404 | Resource not found |
4130 | 413 | File too large |
4290 | 429 | Rate limit exceeded |
1000 | 500 | Internal server error |
Best Practices
1. Keep API Keys Out of Source Code
const apiKey = process.env.BV_API_KEY;2. Use Batch vs File Endpoints Correctly
| Use Case | Endpoint | Limit |
|---|---|---|
| Single real-time verification | /verify/single | 1 email |
| Small synchronous batch | /verify/bulk | 100 emails |
| Large asynchronous processing | /verify/file | 100,000 emails/file |
3. Prefer Webhooks for File Jobs
Polling is supported, but webhooks are a better fit for long-running file verification workflows.