Overview
The LineVerify API checks US phone numbers and email addresses in real time. A phone lookup returns the carrier, line type, spam risk, do-not-call status, lawsuit risk, the name on the line, best time to call and area code location. An email lookup tells you if mail to that address will arrive.
https://api.lineverification.com/v1curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'{
"type": "phone",
"number": "+14155550123",
"status": "valid",
"line_type": "mobile",
"carrier": "Verizon",
"portable": true,
"ported_date": "2021-04-12",
"caller_name": "JOHN SMITH",
"activity_score": 88,
"spam_score": "low",
"dnc_status": "clean",
"dnc_list": null,
"tcpa_risk": "low",
"timezone": "Pacific (PT)",
"location": "San Francisco, CA",
"credits_used": 1,
"credits_remaining": 4823
}Authentication
Send your API key as a Bearer token in the Authorization header on every request. Create and revoke keys in Settings, under API keys. We show a new key once, so copy it when you make it.
Sandbox results are made up and no carrier is contacted. Each number or email always gets the same answer, and different ones get a mix of valid, invalid, risky and landline, so you can test every path.
Never put your API key in browser code. Keep it in an environment variable on your server.
curl https://api.lineverification.com/v1/lookup \ -H "Authorization: Bearer lv_live_YOUR_KEY" \ -H "Content-Type: application/json"
Single lookup
Send one phone number or one email address, get back everything we know about it. Each successful lookup uses 1 check. Fields we could not check come back as null.
"phone""email"curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'{
"type": "phone",
"number": "+14155550123",
"status": "valid",
"line_type": "mobile",
"carrier": "Verizon",
"portable": true,
"ported_date": "2021-04-12",
"caller_name": "JOHN SMITH",
"activity_score": 88,
"spam_score": "low",
"dnc_status": "clean",
"dnc_list": null,
"tcpa_risk": "low",
"timezone": "Pacific (PT)",
"location": "San Francisco, CA",
"credits_used": 1,
"credits_remaining": 4823
}curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}'{
"type": "email",
"email": "jane@example.com",
"status": "valid",
"domain": "example.com",
"deliverability": "deliverable",
"inbox": "Mailbox exists",
"disposable": false,
"did_you_mean": null,
"credits_used": 1,
"credits_remaining": 4822
}Bulk lookup
Send up to 500 phone numbers and email addresses in one request. Bulk lookups run in the background: you get a batch_id straight away, and the results go to your webhook. You can also fetch them with the batch id.
If one entry can't be read, the whole request is rejected with a 400 or 422 and nothing is charged.
curl -X POST \
https://api.lineverification.com/v1/lookup/bulk \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"numbers": ["+14155550123", "+16285550198", "+15105550147"],
"emails": ["jane@example.com"],
"webhook_url": "https://yourapp.com/webhook/lineverify"
}'{
"batch_id": "0b6f3c2e-8d1a-4f5b-9c7e-2a4d6e8f1b3c",
"status": "processing",
"total": 4,
"credits_reserved": 4,
"credits_remaining": 4819
}Batch results
Check on a bulk lookup with its batch_id. While it runs you see how far it has got. Once it is done you get every result, in the same shape as a single lookup and in the order you sent them (numbers first, then emails).
null until thenIf you sent a webhook_url, we post the finished batch to it as JSON: batch_id, status, the total credits_used, and the results, each with its own credits_used. Reply with any 2xx status. If your server doesn't answer, we try twice more, a few seconds apart.
curl https://api.lineverification.com/v1/batch/BATCH_ID \ -H "Authorization: Bearer lv_live_YOUR_KEY"
{
"batch_id": "0b6f3c2e-8d1a-4f5b-9c7e-2a4d6e8f1b3c",
"status": "done",
"total": 4,
"checked": 4,
"results": [
{
"type": "phone",
"number": "+14155550123",
"status": "valid",
"line_type": "mobile",
...
},
{
"type": "email",
"email": "jane@example.com",
"status": "valid",
...
}
],
"credits_remaining": 4820
}POST https://yourapp.com/webhook/lineverify
User-Agent: LineVerify-Webhook/1.0
{
"batch_id": "0b6f3c2e-8d1a-4f5b-9c7e-2a4d6e8f1b3c",
"status": "done",
"credits_used": 3,
"results": [
{
"type": "phone",
"number": "+14155550123",
"status": "valid",
...
"credits_used": 1
},
...
]
}SDKs
No SDK needed. Plain HTTP works from any language. Typed SDKs are on the roadmap.
Switch between curl, Node and Python at the top of the page. Every code sample follows your choice.
curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'Agent use
LineVerify is built to be called by AI agents. Use the tool definition with Claude, GPT-4 or any OpenAI-compatible function calling API. Responses are structured for easy parsing, no prompt engineering needed.
Verify before acting. Have your agent call verify_phone_number before it sends a text, makes a call or adds a contact to a CRM. Only go ahead when status is "valid" and dnc_status is "clean".
{
"name": "verify_phone_number",
"description": "Verify a phone number and return carrier, line type, spam risk, DNC status, TCPA risk, caller name, and timezone. Use before making calls or sending SMS to ensure the number is valid and safe to contact.",
"parameters": {
"type": "object",
"properties": {
"number": {
"type": "string",
"description": "Phone number in E.164 format, e.g. +14155550123"
}
},
"required": ["number"]
}
}MCP server
Use LineVerify as a Model Context Protocol (MCP) server. Drop the server into Claude Desktop or any MCP-compatible host. It speaks the MCP stdio transport directly, so there is nothing to install besides Node.
mcp-server.jsclaude_desktop_config.json with your keyThe server needs Node 18 or later for built-in fetch. Hosts that support remote MCP servers can skip the file and connect to https://www.lineverification.com/api/mcp with your key as a Bearer token.
// mcp-server.js: LineVerify MCP server (stdio transport)
// Implements MCP over stdin/stdout, no SDK required.
// Requires Node 18+ for native fetch.
const KEY = process.env.LV_API_KEY;
async function lvLookup(body) {
const res = await fetch("https://api.lineverification.com/v1/lookup", {
method: "POST",
headers: {
"Authorization": `Bearer ${KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return { ok: res.ok, data: await res.json() };
}
const TOOLS = [{
name: "verify_phone",
description: "Verify a US phone number. Returns status, carrier, line type, spam risk, DNC status, TCPA risk, caller name, and timezone.",
inputSchema: {
type: "object",
properties: {
number: { type: "string", description: "Phone number in E.164 format, e.g. +14155550123" }
},
required: ["number"]
}
}, {
name: "verify_email",
description: "Verify an email address. Returns status, deliverability, whether the inbox exists, and whether it is disposable.",
inputSchema: {
type: "object",
properties: {
email: { type: "string", description: "Email address, e.g. jane@example.com" }
},
required: ["email"]
}
}];
async function handle(msg) {
switch (msg.method) {
case "initialize":
return { protocolVersion: msg.params.protocolVersion, capabilities: { tools: {} },
serverInfo: { name: "lineverify", version: "1.0.0" } };
case "ping":
return {};
case "tools/list":
return { tools: TOOLS };
case "tools/call": {
const { ok, data } = await lvLookup(msg.params.arguments);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }], isError: !ok };
}
}
}
process.stdin.setEncoding("utf8");
let buf = "";
process.stdin.on("data", async chunk => {
buf += chunk;
const lines = buf.split("\n");
buf = lines.pop();
for (const line of lines) {
if (!line.trim()) continue;
const msg = JSON.parse(line);
if (msg.id === undefined) continue; // notifications need no reply
const result = await handle(msg);
const response = result
? { jsonrpc: "2.0", id: msg.id, result }
: { jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "Method not found" } };
process.stdout.write(JSON.stringify(response) + "\n");
}
});{
"mcpServers": {
"lineverify": {
"command": "node",
"args": ["/path/to/mcp-server.js"],
"env": { "LV_API_KEY": "lv_live_YOUR_KEY" }
}
}
}Errors and limits
Errors use standard HTTP status codes, with a JSON body that holds a code you can match on and a message you can show. You can make up to 100 requests a minute with each key. Errors are never billed.
invalid_requestThe body is missing, not JSON, or breaks a limit
invalid_numberThe number could not be read
invalid_emailThe email address could not be read
unauthorizedMissing, invalid or revoked API key
insufficient_creditsNot enough checks left
not_foundNo batch with that id
unsupported_regionThe number is outside the US
rate_limit_exceededMore than 100 requests a minute. Wait and retry
upstream_errorNone of our data providers answered. Not billed, try again
curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'{
"error": {
"code": "insufficient_credits",
"message": "You have no checks left."
}
}Credits
Checks are called credits in the API. They never expire, and your balance comes back as credits_remaining in every successful response. To check it on its own, call GET /v1/balance, which returns credits_remaining, plan and sandbox.
curl -X POST \
https://api.lineverification.com/v1/lookup \
-H "Authorization: Bearer lv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'{
"type": "phone",
"number": "+14155550123",
"status": "valid",
"line_type": "mobile",
"carrier": "Verizon",
"portable": true,
"ported_date": "2021-04-12",
"caller_name": "JOHN SMITH",
"activity_score": 88,
"spam_score": "low",
"dnc_status": "clean",
"dnc_list": null,
"tcpa_risk": "low",
"timezone": "Pacific (PT)",
"location": "San Francisco, CA",
"credits_used": 1,
"credits_remaining": 4823
}