Person V1 → V2
Field mapping and behavior changes when moving from /v1/person to /v2/person.
This guide walks through every input and output change between the V1 and V2 person endpoints so you can migrate cleanly. V2 is a superset of V1: every V1 field has the same name and meaning in V2, with three shape changes called out below. The rest of V2's surface is purely additive — match scoring, fuzzy matching, pagination, per-result metadata.
TL;DR
- Swap
/v1/personfor/v2/personin both your Search and Details calls. - Read results from
response.resultsinstead of the top-level array (Search only — Details is unchanged at the envelope level). - Update email reads from
emails[i](a string) toemails[i].email(an object with a confidencescore). - Everything else just keeps working. Optional: start parsing
match_score,matched_by, andresult_metadatato take advantage of new V2 capabilities.
Endpoint URLs
| Operation | V1 | V2 |
|---|---|---|
| Search | GET /v1/person/?... | GET /v2/person/?... |
| Details | GET /v1/person/{id} | GET /v2/person/{id} |
The Whitepages person ID format is unchanged (P prefix + 10
alphanumeric characters).
Request parameters
Every V1 search parameter works unchanged in V2 with the same name, type, and accepted values:
phone, name, first_name, middle_name, last_name, street,
city, state_code, zipcode, radius, min_age, max_age,
include_historical_locations.
V2 adds four optional parameters:
| Parameter | Type | Default | Purpose |
|---|---|---|---|
email | string | — | Search by email address. V1 has no email lookup. |
include_fuzzy_matching | boolean | false | Broaden name matching to phonetic variants and common nicknames. |
page | integer | 1 | Page number, 1–10. Pages 2+ are billable per the billing policy. |
page_size | integer | 15 | Results per page, 1–15. |
GET /v2/person/{id} continues to take the person ID in the path with
no additional parameters.
Response shape (Search)
V1
A V1 Search response is a JSON array of up to 15 person records:
[
{
"id": "P1234567890",
"name": "John Smith",
"phones": [{ "number": "(212) 555-0198", "type": "mobile", "score": 92 }],
"emails": ["john.smith@example.com"],
"current_addresses": [...],
"historic_addresses": [...],
"owned_properties": [...],
"relatives": [...],
"aliases": ["Johnny Smith"],
"is_dead": false,
"date_of_birth": "1985-03-15",
"linkedin_url": "https://linkedin.com/in/johnsmith",
"company_name": "Acme Corp",
"job_title": "Software Engineer"
}
]V2
A V2 Search response wraps the results in a results array and adds a
top-level metadata block plus a per-result result_metadata block:
{
"results": [
{
"id": "P1234567890",
"name": "John Smith",
"phones": [{ "number": "(212) 555-0198", "type": "mobile", "score": 92 }],
"emails": [{ "email": "john.smith@example.com", "score": 88 }],
"current_addresses": [...],
"historic_addresses": [...],
"owned_properties": [...],
"relatives": [...],
"aliases": ["Johnny Smith"],
"is_dead": false,
"date_of_birth": "1985-03-15",
"linkedin_url": "https://linkedin.com/in/johnsmith",
"company_name": "Acme Corp",
"job_title": "Software Engineer",
"match_score": 87,
"matched_by": ["name", "address"],
"result_metadata": {
"details_url": "/v2/person/P1234567890",
"phones": { "displayed": 1, "additional": 0 },
"emails": { "displayed": 1, "additional": 0 },
"historic_addresses": { "displayed": 1, "additional": 0 },
"owned_properties": { "displayed": 1, "additional": 0 }
}
}
],
"metadata": {
"result_count": 432,
"page": 1,
"page_size": 15
}
}Response shape (Details)
GET /v2/person/{id} returns a single record wrapped in result,
matching the existing pattern for V2 detail endpoints:
{
"result": {
"id": "P1234567890",
"name": "John Smith",
"...": "...same fields as a Search result, minus result_metadata, match_score, matched_by"
}
}No result_metadata on Details
The result_metadata block is exclusive to Person Search. Person Details
continues to return the full lists with no summary block.
Field-by-field mapping
Every V1 person-record field exists in V2 with the same name and meaning. Three shapes changed:
| V1 | V2 | What changed |
|---|---|---|
Top-level: [record, record, ...] | Top-level: { "results": [...], "metadata": {...} } | Search response is wrapped. Read response.results to get the V1-equivalent list. Pagination (metadata.result_count, .page, .page_size) and per-result result_metadata are additive. |
emails: ["a@example.com", "b@example.com"] | emails: [{ "email": "a@example.com", "score": 95 }, { "email": "b@example.com", "score": 60 }] | Emails are now objects with a confidence score (0–100). Read .email to recover the V1 string. The list is sorted by score descending. |
(no match_score) | match_score: integer | null (1–99) | New on Search only. Higher scores indicate stronger relevance + completeness. |
(no matched_by) | matched_by: list[string] | New on Search only. Names the input field types that contributed to this result (e.g., ["phone"], ["name", "address"]). |
(no result_metadata) | result_metadata: {...} | New on Search only. See result_metadata below. |
All other fields — id, name, aliases, is_dead, phones[],
current_addresses[], historic_addresses[], owned_properties[],
relatives[], date_of_birth, linkedin_url, company_name,
job_title — keep their V1 type, nullability, and semantics.
V2-only features
Pagination
V1 returned up to 15 results per call with no way to reach beyond them. V2 supports paging through up to 10 pages of 15 results each (150 total ceiling).
curl 'https://api.whitepages.com/v2/person?last_name=Smith&page=2&page_size=15' \
--header 'X-Api-Key: YOUR_API_KEY'response.metadata.result_count reports the total number of matching
records, and metadata.page / metadata.page_size echo the page you
fetched. See Pagination for
details. Each page after the first is a separate billable request.
Match scoring
V2 results include a match_score (1–99) that ranks results by how
closely they match your input across relevance and completeness, plus
a matched_by array that names which input field types contributed.
Use these to prioritize the most likely matches in your UI or pipeline.
{ "id": "P1234567890", "match_score": 87, "matched_by": ["name", "address"] }Fuzzy / nickname matching
Set include_fuzzy_matching=true on Search to broaden name matching
beyond exact spelling — phonetic variants, common nicknames, and
typos. Useful when input names come from human entry (CRM notes,
voicemail transcripts).
Confidence-scored emails
V2 emails are objects with a score (0–100) so you can prioritize the
most likely-current address for outreach.
result_metadata on every Search result
Every Search result carries a result_metadata object summarizing each
list and linking back to the canonical Person Details URL:
"result_metadata": {
"details_url": "/v2/person/P1234567890",
"phones": { "displayed": 3, "additional": 0 },
"emails": { "displayed": 1, "additional": 0 },
"historic_addresses": { "displayed": 4, "additional": 0 },
"owned_properties": { "displayed": 0, "additional": 0 }
}displayed is the count of items in the corresponding list on this
response; additional is the count linked to the person but not
included. Today additional is always 0 — V2 Search still returns
the full upstream lists. A future release will narrow each list to a
small kept window; additional will then tell you how many more
entries are available at details_url (GET /v2/person/{id}).
details_url is null for the rare result that arrives without a
canonical Whitepages person ID (e.g., an unresolved phone-owner
record). Those records are exempt from the upcoming list-narrowing —
they are returned with their full lists regardless.
See the Changelog for the latest release notes on
result_metadata behavior.
Behavioral differences
| Behavior | V1 | V2 |
|---|---|---|
| Result cap | Hard cap at 15 per request, no pagination. | Up to 10 pages × 15 results = 150 total. |
| Result ordering | Returned in upstream Search-Layer order; no ranking signal. | Sorted by match_score descending where applicable. |
| Email search | Not supported. | New email query parameter. |
| Empty results | Empty array [] with HTTP 200. | { "results": [], "metadata": { "result_count": 0, ... } } with HTTP 200. |
| Phone scoring | Phones include score (0–100). Unchanged. | Same. |
| 404 on Details | Billable in V1. | Same. (Note: V2 Property 404 on Details is not billable; Person Details 404 is still billable. See Billing.) |
Forward compatibility notes
V2 is the long-term home for the person endpoints. Two upcoming changes are worth knowing about while you migrate:
-
Search will behave more like search. Today a Person Search response carries every linked phone, email, historic address, and owned property for each matched record — useful data, but more than most Search use cases need to identify a match and decide whether to drill in. A future release tunes Search to a lean, ranked preview per list: the items most likely to inform the match-vs-traverse decision, in priority order. Payloads shrink, results are easier to scan, and Person Details (
details_url) continues to carry the full lists for callers who want everything.result_metadata.{list}.additionalis the forward-compatible signal — it goes from0today to a non-zero count when the tuning ships, telling you how many more items are available on Details. The change has a 30-day customer notice window before it ships; subscribe to the Changelog to get the heads-up. -
No new V2-only required fields. All current V2 fields preserve V1's nullability contracts. Future additive changes will follow the same pattern.
Call translation examples
Search by phone
V1:
curl 'https://api.whitepages.com/v1/person?phone=2125550198' \
--header 'X-Api-Key: YOUR_API_KEY'V2:
curl 'https://api.whitepages.com/v2/person?phone=2125550198' \
--header 'X-Api-Key: YOUR_API_KEY'The only required change is the URL path. The V2 response wraps results
in { "results": [...], "metadata": {...} }.
Search by name and city, page 2
V1 had no pagination — your only option was a single tighter query. V2:
curl 'https://api.whitepages.com/v2/person?last_name=Smith&city=Seattle&state_code=WA&page=2' \
--header 'X-Api-Key: YOUR_API_KEY'Pull full details for a confirmed match
V1:
curl 'https://api.whitepages.com/v1/person/P1234567890' \
--header 'X-Api-Key: YOUR_API_KEY'V2:
curl 'https://api.whitepages.com/v2/person/P1234567890' \
--header 'X-Api-Key: YOUR_API_KEY'V2 Details responses are wrapped in { "result": {...} }. The fields
inside are a superset of V1 (no removals, no nullability changes).
Code translation snippets
JavaScript (Search)
const response = await fetch(
`https://api.whitepages.com/v1/person?name=${name}`,
{
headers: { "X-Api-Key": API_KEY },
},
);
const records = await response.json();
for (const person of records) {
console.log(person.name, person.emails[0]);
}const response = await fetch(
`https://api.whitepages.com/v2/person?name=${name}`,
{
headers: { "X-Api-Key": API_KEY },
},
);
const { results, metadata } = await response.json();
for (const person of results) {
console.log(
person.name,
person.emails[0]?.email,
"match:",
person.match_score,
);
}
console.log(`Total matches across all pages: ${metadata.result_count}`);Python (Details)
import requests
# V1
r = requests.get(
"https://api.whitepages.com/v1/person/P1234567890",
headers={"X-Api-Key": API_KEY},
)
record = r.json() # V1 returns the record directly
# V2
r = requests.get(
"https://api.whitepages.com/v2/person/P1234567890",
headers={"X-Api-Key": API_KEY},
)
record = r.json()["result"] # V2 wraps in {"result": ...}Need help?
If you hit a field that doesn't map cleanly, or you'd like guidance on
adopting match_score / result_metadata in your pipeline, reach out
via support.