Enrich Contacts
Find work emails, personal emails and phone numbers with POST /v1/enrich/email and POST /v1/enrich/phone.
POST https://api.jobspipe.dev/v1/enrich/email
POST https://api.jobspipe.dev/v1/enrich/phoneGiven up to 100 people, each named by a LinkedIn profile URL or by full name and company domain, returns a verified work email, a personal email or a phone number for each one found. You pay only for contacts found: a miss costs nothing, and a contact your account revealed before comes back free. Requires authentication.
Try it live in the API Explorer.
Request
The body is a JSON object with an items array of 1 to 100 people. Each item needs linkedin_url, or full_name and domain.
| Field | Type | Description |
|---|---|---|
linkedin_url | string | The person's LinkedIn profile URL. Needed to find a phone or a personal email. |
full_name | string | The person's full name. Send it with domain. |
domain | string | The company's website domain, e.g. acme.com. Send it with full_name. |
type | string | Email only. work (default) or personal. Phone items take no type. |
A name and domain item can find a work email only: for a phone or a personal email it comes back not_found, free.
Send an optional Idempotency-Key header (a UUID, say) to make a request safe to retry. A retry with the same key within 24 hours replays the first response, with an Idempotent-Replayed: true header, and charges nothing. A duplicate sent while the first is still running gets 409 with {"error": "idempotency_conflict"}. A 402 (balance too low) and that 409 are never replayed, so the same key works again once you add credits or the first request returns.
curl https://api.jobspipe.dev/v1/enrich/email \
-H "Authorization: Bearer jp_live_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2a4e-3b7d-4e8a-9c1f-0d2b5e7a9c31" \
-d '{
"items": [
{ "linkedin_url": "https://www.linkedin.com/in/jane-doe" },
{ "full_name": "Sam Lee", "domain": "acme.com" },
{ "linkedin_url": "https://www.linkedin.com/in/alex-kim", "type": "personal" }
]
}'import requests
resp = requests.post(
"https://api.jobspipe.dev/v1/enrich/phone",
headers={"Authorization": "Bearer jp_live_your_key_here"},
json={"items": [{"linkedin_url": "https://www.linkedin.com/in/jane-doe"}]},
)
resp.raise_for_status()
body = resp.json()
for r in body["results"]:
print(r["index"], r["status"], r["value"])
print("credits charged:", body["credits_charged"])const resp = await fetch("https://api.jobspipe.dev/v1/enrich/email", {
method: "POST",
headers: {
Authorization: "Bearer jp_live_your_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [{ full_name: "Sam Lee", domain: "acme.com" }],
}),
});
const body = await resp.json();
for (const r of body.results) {
console.log(r.index, r.status, r.value);
}Response
A 200 response has one result per item, in the order sent:
results- each withindex(the item's position, from 0),status,type(work,personalorphone),value(the email or phone, ornull) andcredits(what this item cost).found- items with a value (enrichedorcached).credits_charged- what the whole batch cost. One charge covers only the found items.
| Status | Meaning | Cost |
|---|---|---|
enriched | Found now. | The contact's price |
cached | Your account revealed this contact before. | Free |
not_found | No contact found for this person. | Free |
insufficient_credits | Your balance ran out before this item; it was not looked up. | Free |
rate_limited | Lookups are busy; try this item again shortly. | Free |
error | The lookup failed; try again. | Free |
{
"results": [
{ "index": 0, "status": "enriched", "type": "work", "value": "jane.doe@acme.com", "credits": 15 },
{ "index": 1, "status": "not_found", "type": "work", "value": null, "credits": 0 },
{ "index": 2, "status": "cached", "type": "personal", "value": "alex.kim@gmail.com", "credits": 0 }
],
"found": 2,
"credits_charged": 15
}Pricing
| Contact | Credits per contact found |
|---|---|
| Work email | 15 |
| Personal email | 10 |
| Phone | 100 |
A contact your account already revealed, in the API or the dashboard, is free every time it comes back. When your balance covers only part of a batch, the items it does not cover come back insufficient_credits and cost nothing. See plans.
Status codes
| Status | Meaning |
|---|---|
200 | Success, with a status per item |
400 | No items, more than 100 items, an item with neither linkedin_url nor full_name and domain, or an unknown type |
401 | Missing or invalid API key |
402 | Your balance does not cover even one contact ({"error": "insufficient_credits", "message", "upgrade_url"}) |
409 | A request with the same Idempotency-Key is still running |
429 | Per-second rate limit exceeded |
See the error reference for handling guidance.