Call-center screening checks phone numbers before or during a call, so agents and diallers spend their time on real contacts. The carrier lookup reports the line type, the carrier and the country. Where it's enabled, spam reputation reports whether a US, Canadian or German number appears in spam and nuisance-call reports, with the reasons. Spam reputation is in limited access for now (internal customers only).
What problems does screening solve?
Contact centers lose time and money in both directions:
- Inbound. Robocalls and nuisance calls tie up agents and IVR minutes. Scam callers pretend to be customers. A reputation signal lets you send high-risk calls to an IVR or a verification step instead of a live agent.
- Outbound. Dialling lists contain fixed lines where you expected mobiles, numbers that were recently offered as unassigned, and numbers with fraud reports. Checking them first protects agent time and your caller reputation.
- Lead intake. Web leads with a spoofed or recently unassigned number are a common sign of fake leads. See lead verification.
Spam reputation works from report classes described in general terms: telecom regulator actions, government nuisance-call complaint data, community reports and a signal for recently unassigned numbers. There's also a VoIP hint, which is not a risk by itself. Every level comes with the reasons behind it. Report texts and reporter details are never returned.
How does the workflow look?
Inbound calls:
- Your telephony platform receives the caller ID.
- Call
POST /v1/lookupwithchecks: ["spam", "carrier"]and a shortwait. - Route the call using the table below and log the level, not the raw response.
Outbound lists:
- Run a bulk job with
checks: ["carrier"]. For US and Canadian lists, addnetwork.carrier_us, and addspamif it's enabled for your account. - Download the CSV. Each check has its own columns, such as
number.spam.risk_levelandnetwork.carrier.line_type. - Remove or reorder rows before they reach the dialler.
What should you do with each result?
| Result | Suggested action |
|---|---|
risk_level: high (score ≥ 80 and a regulator action or two or more signal classes) | Inbound: IVR or verification step. Outbound: remove from the list |
risk_level: medium | Inbound: ask for extra verification. Outbound: review |
risk_level: low | Handle normally and watch for patterns |
risk_level: no_reports | No negative signal. Not proof of safety |
reason_unassigned: true | Treat as a possible spoofed caller ID or fake lead |
line_type: fixed_line on a mobile campaign | Move to the voice-only queue |
unknown / unsupported_country | Handle normally; not charged |
How much does it cost?
You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). For spam reputation, every level, including no_reports, is a conclusive answer and is billed. Numbers outside the US, Canada and Germany return unsupported_country for free. The carrier lookup is billed when it returns a carrier. When no carrier data exists for a number, the answer is unknown and free. See pricing for current rates.
Spam answers come from our own reference data, which is refreshed daily. That keeps real-time answers fast, and repeat checks within 24 hours are served from your account's cache for free. That matters for repeat callers.
Example request
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001"],"checks":["spam","carrier"],"wait":3}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.lookup({
numbers: ["+447700900001"],
checks: ["spam", "carrier"],
wait: 3,
});
if (error) console.error(error.code, error.message);
else console.dir(data.results, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/lookup", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
numbers: ["+447700900001"],
checks: ["spam", "carrier"],
wait: 3,
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.lookup(numbers=["+447700900001"], checks=["spam", "carrier"], wait=3)
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/lookup');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'numbers' => ['+447700900001'],
'checks' => ['spam', 'carrier'],
'wait' => 3,
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"numbers":["+447700900001"],"checks":["spam","carrier"],"wait":3}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/lookup", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/lookup")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"numbers" => ["+447700900001"],
"checks" => ["spam", "carrier"],
"wait" => 3
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyRuns as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
In test mode the whole +44 7700 900xxx test range is allowed for spam reputation, so the documented numbers work. Response (excerpt, test mode: checks of the first item):
{
"number.spam": {
"service": "number.spam", "status": "completed", "registered": true,
"attributes": {
"risk_level": "high", "risk_score": 95, "reason_regulator": true, "reason_government": false,
"reason_community": true, "reason_unassigned": false, "voip_range": false, "top_category": "robocall",
"first_seen": "2025-11", "last_seen": "2026-08", "sources": 2
},
"confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-25T14:28:24.778Z",
"cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
},
"network.carrier": {
"service": "network.carrier", "status": "completed", "registered": true,
"attributes": {"line_type": "mobile", "carrier": "Test Carrier", "country": "GB"},
"confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-25T14:28:24.778Z",
"cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
}
}For attributes services, registered: true means "data found". The answer is in attributes.
What limits and rules apply?
A lookup takes up to 100 numbers and a job up to 50,000. Requests that look like sequential number ranges or generated e-mail lists are rejected, and each account has a daily cap on numbers. Spam scores can change as new reports arrive and old ones age out, so read checked_at.
A reputation level is a signal about a number, not a judgment about a person. Numbers get spoofed and reassigned. Always give legitimate callers a way through, for example a verification step. Don't use the results for credit, employment, housing or insurance decisions. People who find their number in our data can object or ask for access through the opt-out form and the data-subject notice.
Frequently asked questions
Is the spam reputation check available to every customer?
Not yet. Spam reputation is in limited access, for internal customers only, until its review is finished. The carrier lookup is available to all customers.
Which countries does spam reputation cover?
The United States, Canada and Germany, where report data is dense. Numbers from other countries return unsupported_country and are not charged.
Does no_reports mean a caller is safe?
No. It means we hold no reports for the number. New, rarely used or spoofed numbers can still be abusive. Treat it as no negative signal and combine it with other checks.
Is a no_reports answer charged?
Yes. Every risk level, including no_reports, is a conclusive answer and is billed. Unknown and unsupported-country results are free.
Can the carrier lookup tell me who is calling?
No. It returns line type, carrier and country for the number. It never returns names or any other details about the person behind the number.
Related
Guides on this topic
All articlesFraud prevention
Screening inbound calls with spam reputation
A call-center workflow for routing inbound calls by spam reputation: levels, reasons, STIR/SHAKEN, limits of the data, and why no_reports isn't safe.
7 min read
Research
US nuisance-call complaints in 2026: what the public data shows
Our analysis of 362,116 FTC Do Not Call complaints and FCC unwanted-call data: top subjects, robocall share, weekday pattern and trend.
8 min read

