REST API for programmatic people search. Query by name and optional age, city, and country — get a full profile back as JSON.
The PersonPages API returns the same structured profile you'd get from a normal search on the site. Results are cached, so a repeated query for the same person is served instantly and doesn't cost extra credits.
https://personpages.comAuthorization headerEvery request must include your API key as a bearer token. Keys start with pp_live_ (or pp_test_ for sandbox keys) and are shown once at signup — treat them like a password.
Authorization: Bearer pp_live_xxxxxxxxxxxxxxxxxxxxxxxx
Lost your key? You can rotate it from your developer dashboard.
Look up a person. Returns the full profile plus a shareable URL.
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Full name, 2–120 chars. |
| age | integer | No | Age, 13–120. Improves match quality. |
| city | string | No | City name, up to 120 chars. |
| country | string | No | Country name, up to 120 chars. |
{
"ok": true,
"cached": false,
"profile_url": "https://personpages.com/profile/jane-doe-berlin",
"quota": {
"plan": "starter",
"limit": 2000,
"used": 12,
"remaining": 1988,
"resets_at": "2026-08-01T00:00:00.000Z"
},
"profile": {
"slug": "jane-doe-berlin",
"full_name": "Jane Doe",
"employer": "...",
"city": "Berlin",
"country": "Germany",
"...": "all fields returned by the profile page"
}
}cached: when true, the profile already existed and no new generation ran. Cached lookups still count as one call against your monthly quota.
curl -X POST https://personpages.com/api/public/v1/lookup \
-H "Authorization: Bearer pp_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"age": 34,
"city": "Berlin",
"country": "Germany"
}'const res = await fetch("https://personpages.com/api/public/v1/lookup", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.PERSONPAGES_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Jane Doe",
city: "Berlin",
country: "Germany",
}),
});
const data = await res.json();
console.log(data.profile_url, data.profile);import os, requests
r = requests.post(
"https://personpages.com/api/public/v1/lookup",
headers={"Authorization": f"Bearer {os.environ['PERSONPAGES_KEY']}"},
json={"name": "Jane Doe", "city": "Berlin", "country": "Germany"},
timeout=60,
)
r.raise_for_status()
data = r.json()
print(data["profile_url"], data["profile"])Errors are returned as JSON with a machine-readable error code and, where useful, a human message.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json | Request body wasn't valid JSON. |
| 400 | invalid_input | Body failed validation. See details. |
| 401 | missing_api_key | No Authorization header. |
| 401 | invalid_api_key | Key not recognized. |
| 403 | api_key_disabled | Key exists but is deactivated. |
| 429 | quota_exceeded | Monthly quota used up. Upgrade or wait for reset. |
| 502 | generation_failed | Upstream provider error. Safe to retry. |
| 500 | server_error | Unexpected server error. |
quota.used by 1.Does the API return the same data as the website?
Yes. The same profile object powers both the public profile page and the API response.
Are new profiles created when I use the API?
Yes. If the person isn't in our index, the API triggers the same generation pipeline as a normal search, and a public profile page is created at the returned profile_url.
Is there a sandbox / test environment?
Test-mode keys start with pp_test_ and are issued when you sign up with a Stripe test card. They hit the same endpoint and behave identically.
Rate limits?
The monthly quota is the primary throttle. If you need burst-safe rate limits or an SLA, contact us.