API
The same data behind every page on this site, over HTTP. No account, no key, and no sign-up for ordinary use — see higher limits if that changes for you.
Base URL
https://whocalledyou.dev/api/v1
Look up a number
GET /lookup?phone=4045551234
Any common US format works — the same parsing the search box uses. A number that was never reported and one whose holder removed it (opted out) return identical responses; there is no way to tell them apart from outside, by design.
{
"phone_number": "+14045551234",
"status": "reported",
"report_count": 12,
"reporter_count": 9,
"categories": {"telemarketing": 8, "robocall": 4},
"first_reported_at": "2026-03-14",
"last_reported_at": "2026-08-22"
}
status is one of:
no_reports— including a number that was reported and then removed;few_reportsreportedfrequently_reporteddisputed— the number's holder has formally contested the reports.
categories only lists categories with at least one report.
The full set is telemarketing, robocall,
scam, fraud, debt_collection,
political, survey, wrong_number, and
other — the last two are shown but never move a number's
status (why).
An unusable number returns 400. Too many requests returns
429 with a Retry-After header:
{"error_code": "rate_limited", "message": "Too many lookups. Try again shortly."}
Site totals
GET /stats
{"numbers": 985034, "reports": 1138221}
Aggregate counts only — no breakdown by number, no recent activity, nothing that could be walked into a directory of who has been reported.
Submit a report
POST /reports
Content-Type: application/json
{"phone": "4045551234", "category": "robocall", "challenge_token": "..."}
description is optional, up to 2,000 characters, kept for
moderation, and never published (why).
challenge_token is a
Cloudflare
Turnstile token proving a human submitted the request — the same check
the report form on the site runs. In practice this makes the endpoint
reachable from a browser, not from a plain server-to-server client: there
is no way to obtain a valid token without rendering the challenge.
Responses: 201 accepted; 400 unusable number;
403 challenge failed; 409 already reported this
number in the last 30 days; 429 rate limited.
Health
GET /health
App and database status. No authentication, not rate limited.
Rate limits
| Lookups | 60/minute, 1000/day |
|---|---|
| Reports | 5/hour, 20/day, per person |
Limits key off a salted, expiring hash of the caller's address — the same mechanism used for reporter deduplication — never a durable identifier.
Schema
A machine-readable OpenAPI document describes every endpoint and response shape above, for code generation, typed clients, or an agent's own tool definitions.
Use it from an AI assistant
The same two read-only operations are exposed over the Model Context Protocol, so an assistant can look a number up on your behalf without anyone writing integration code:
https://whocalledyou.dev/mcp
In Claude, that goes in Settings → Connectors → Add custom connector. From the command line:
claude mcp add --transport http whocalledyou https://whocalledyou.dev/mcp
Two tools, both read-only:
lookup_number— one number's reports, the same data/lookupreturns;site_stats— the totals from/stats.
Submitting a report is deliberately not available this way: it requires the human check described above, and a token for it can only be obtained in a browser.
That address is not a web page — opening it in a browser returns a protocol error rather than anything readable, which is expected. It is meant to be pasted into a client. The same rate limits apply as to the HTTP API; both paths share one budget rather than one being a way around the other.
Higher limits or commercial use
The limits above are sized for occasional use. Building something that needs more — checking numbers at volume, integrating this into another product — email [email protected]. The terms, including what this data may and may not be used for, apply the same way regardless of volume.