Batch Processing
Submit up to 100 phone numbers or URLs in a single request using the ScamVerify™ batch API endpoints.
The ScamVerify™ API supports batch processing for phone lookups and URL verifications. Instead of making individual API calls, you can submit up to 100 items in a single request and receive all results at once.
Batch Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v1/batch/phone | POST | Batch phone number lookup |
/api/v1/batch/url | POST | Batch URL verification |
Batch endpoints are fixed at 5 RPM for all tiers. Since each batch can contain up to 100 items, this allows up to 500 lookups per minute even at the batch rate limit.
Request Format
Submit an array of items in the request body. Each item is processed independently.
curl -X POST https://scamverify.ai/api/v1/batch/phone \
-H "Authorization: Bearer sv_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "phone_number": "+12025551234" },
{ "phone_number": "+13105559876" },
{ "phone_number": "+14155550123" }
]
}'Limits
- Maximum 100 items per batch request
- 10 concurrent lookups are processed internally for faster throughput
- Each item that is not already cached consumes 1 quota from the relevant channel
Response Format
The response includes individual results for each item, plus a summary:
{
"results": [
{
"input": "+12025551234",
"risk_score": 82,
"verdict": "high_risk",
"explanation": "This number has 14 FTC complaints...",
"cached": false,
"cache_expires_at": "2026-03-10T14:30:00Z"
},
{
"input": "+13105559876",
"risk_score": 15,
"verdict": "low_risk",
"explanation": "No complaints found...",
"cached": true,
"cache_expires_at": "2026-03-10T08:15:00Z"
},
{
"input": "+14155550123",
"error": {
"code": "validation_error",
"message": "Invalid phone number format"
}
}
],
"summary": {
"total": 3,
"succeeded": 2,
"failed": 1,
"high_risk": 1,
"medium_risk": 0,
"low_risk": 1
},
"quota_used": 1
}Result Fields
Each item in the results array contains either a successful lookup result or an error object. Successful results have the same fields as individual lookup responses (risk_score, verdict, explanation, cached, cache_expires_at, and channel-specific signals). Failed items include an error object with code and message.
Summary Fields
| Field | Description |
|---|---|
total | Total number of items submitted |
succeeded | Number of items that returned a result |
failed | Number of items that returned an error |
high_risk | Items with verdict high_risk |
medium_risk | Items with verdict medium_risk |
low_risk | Items with verdict low_risk |
Quota Usage
The quota_used field shows how many quota units were consumed by the batch. Cached results do not consume quota, so quota_used may be less than succeeded.
Partial Quota Exhaustion
If your quota runs out mid-batch, the items already processed will return results, and the remaining items will return error objects:
{
"results": [
{ "input": "+12025551234", "risk_score": 82, "verdict": "high_risk", "..." : "..." },
{ "input": "+13105559876", "risk_score": 15, "verdict": "low_risk", "..." : "..." },
{
"input": "+14155550123",
"error": {
"code": "quota_exhausted",
"message": "Phone lookup quota exhausted mid-batch"
}
}
],
"summary": { "total": 3, "succeeded": 2, "failed": 1, "..." : "..." },
"quota_used": 2
}When quota exhaustion occurs mid-batch, the order of processing is not guaranteed. Some items may succeed while others fail, regardless of their position in the input array.
Code Examples
import requests
API_KEY = "sv_live_abc123..."
BASE_URL = "https://scamverify.ai/api/v1"
# Batch phone lookup
phone_numbers = [
"+12025551234",
"+13105559876",
"+14155550123",
"+16505551111",
"+17185552222",
]
response = requests.post(
f"{BASE_URL}/batch/phone",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"items": [{"phone_number": num} for num in phone_numbers]
},
)
data = response.json()
# Process results
for result in data["results"]:
if "error" in result:
print(f"{result['input']}: ERROR - {result['error']['message']}")
else:
print(f"{result['input']}: {result['verdict']} (score: {result['risk_score']})")
# Check summary
summary = data["summary"]
print(f"\n{summary['succeeded']}/{summary['total']} succeeded, "
f"{summary['high_risk']} high risk, "
f"{data['quota_used']} quota used")const API_KEY = "sv_live_abc123...";
const BASE_URL = "https://scamverify.ai/api/v1";
// Batch phone lookup
const phoneNumbers = [
"+12025551234",
"+13105559876",
"+14155550123",
"+16505551111",
"+17185552222",
];
const response = await fetch(`${BASE_URL}/batch/phone`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
items: phoneNumbers.map((num) => ({ phone_number: num })),
}),
});
const data = await response.json();
// Process results
for (const result of data.results) {
if (result.error) {
console.log(`${result.input}: ERROR - ${result.error.message}`);
} else {
console.log(`${result.input}: ${result.verdict} (score: ${result.risk_score})`);
}
}
// Check summary
const { summary, quota_used } = data;
console.log(
`\n${summary.succeeded}/${summary.total} succeeded, ` +
`${summary.high_risk} high risk, ` +
`${quota_used} quota used`
);When to Use Batch vs Individual Lookups
Use batch when:
- You have a list of numbers or URLs to check (CSV import, database scan, etc.)
- You want to minimize the number of HTTP connections
- You need a summary of risk distribution across a set of items
Use individual lookups when:
- You need real-time results for a single item (user-facing search)
- You want to process items as they arrive rather than collecting them first
- You need the fastest possible response time for each item
Chunking Large Lists
If you have more than 100 items, split them into chunks and send multiple batch requests. Remember to respect the 5 RPM batch rate limit.
import time
def batch_lookup_all(phone_numbers, api_key, chunk_size=100):
results = []
chunks = [phone_numbers[i:i + chunk_size] for i in range(0, len(phone_numbers), chunk_size)]
for i, chunk in enumerate(chunks):
if i > 0:
time.sleep(12) # Stay within 5 RPM limit
response = requests.post(
f"{BASE_URL}/batch/phone",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={"items": [{"phone_number": num} for num in chunk]},
)
data = response.json()
results.extend(data["results"])
return results