Data catalog · Proxycurl Migration
Proxycurl resolved an email address to a person in one request. RichAPI does it in two: POST /find_linkedin_url_by_email returns the profile URL, then POST /enrich_profile turns that into the record. Two calls, two charges, and both of them bill on a 2xx even when the answer is empty. Price the chain, not the call.
Last updated September 22, 2026
Proxycurl's Person Lookup took an email address and gave you back a person. The replacement is a chain: `POST /find_linkedin_url_by_email` resolves the address to a LinkedIn profile URL, then `POST /enrich_profile` turns that URL into the record. **[Get 25 free credits — no card](https://app.richapi.ai)** Two calls means two charges. Everything on this page prices the chain rather than the call, because a per-call number would understate what your job actually spends.
```bash # Before (Proxycurl) — dead, one request # GET https://nubela.co/proxycurl/api/linkedin/profile/resolve/email?email=<addr> # -H "Authorization: Bearer $PROXYCURL_KEY" # # After (RichAPI), call 1 of 2 — email to profile URL curl -X POST https://api.richapi.ai/api/v1/find_linkedin_url_by_email \ -H "x-api-key: $RICHAPI_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme.com"}' # After (RichAPI), call 2 of 2 — profile URL to person record curl -X POST https://api.richapi.ai/api/v1/enrich_profile \ -H "x-api-key: $RICHAPI_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://www.linkedin.com/in/example/"}' ``` The `email` field is validated against an email pattern, so a malformed address fails as a 4xx and costs nothing. That is worth using: filter obvious garbage before the call and the API will reject the rest for free.
The lookup returns a wrapper, not a flat object: ```json { "status": "...", "data": { "LinkedIn Profile URL": "https://www.linkedin.com/in/example/" } } ``` That key is the literal string `LinkedIn Profile URL`, spaces and capitals included. It is not `linkedin_url`, and your JSON parser will not guess it for you. In TypeScript that means bracket access, `body.data["LinkedIn Profile URL"]`, and in a typed client it means an explicit alias in the model rather than a snake-case convention. Getting this wrong produces `undefined` flowing into the second call, which then fails validation, which then reads like a coverage problem when it is a typo. The second call returns the person record. Its fields are documented on [the Person Profile migration page](/api/proxycurl-person-profile-endpoint), which is the same endpoint with the same mapping work.
```python import os, requests BASE = "https://api.richapi.ai/api/v1" H = {"x-api-key": os.environ["RICHAPI_KEY"]} def person_from_email(email: str) -> dict | None: r = requests.post(f"{BASE}/find_linkedin_url_by_email", headers=H, json={"email": email}) r.raise_for_status() # 4xx/5xx cost nothing, so fail loudly url = (r.json().get("data") or {}).get("LinkedIn Profile URL") if not url: # 2xx with no match — already billed return None p = requests.post(f"{BASE}/enrich_profile", headers=H, json={"url": url}) p.raise_for_status() return p.json() ``` The early return matters for your bill as well as your logic. When the first call resolves nothing, stop. You have already paid for that call and there is no reason to pay for a second one against an empty URL.
`4 credits` for `/find_linkedin_url_by_email`, plus `1 credit` for `/enrich_profile`. A resolved email that ends in a full person record costs the sum of the two, `5 credits`. Rates are tiered by package size on [the pricing page](/pricing). **Neither endpoint is a waterfall, and both bill on any 2xx, including one that found nothing.** An email that resolves to no profile still costs you the first call. Across a list of consumer addresses and long-dead work emails that adds up fast, and it is the number to model before you run 100,000 rows. Our zero-cost-miss endpoints are the waterfalls, [email finder](/api/email-finder), [email verifier](/api/email-verifier) and [phone finder](/api/phone-finder), and none of them is in this chain. 4xx and 5xx bill nothing anywhere. Two ways to spend less on the same job. Skip call one for every record where you already store a LinkedIn URL beside the email, which in most CRMs is a meaningful share of the table. And cache the resolution: an email-to-profile mapping ages slowly compared with a job title, so re-resolving the same address every run is money spent re-learning something you knew.
Both calls return an `execution_log` showing which providers ran and what each returned. On an email that resolves to nothing, that log tells you whether a source was asked and had no record, which is a more useful answer than a silent empty payload.
**Turning inbound signups into real accounts.** A trial signup gives you a work email and nothing else. The chain fills in title, company and seniority before routing, which decides whether a human sees the account today or next quarter. **Reattaching identity to an old list.** A five-year-old export has emails and job titles that are now fiction. Resolve to a profile, re-enrich, and you learn who changed employer rather than emailing a bounced address about a role they left. **Support and fraud triage.** An unfamiliar address writes in claiming to be a buyer at a named company. One chain run tells you whether that person exists there, before anyone commits time or approves anything.
**Why isn't this one call?** Because resolution and enrichment are two different lookups and we bill them separately rather than hiding one inside the other. The upside is that you can skip the resolution whenever you already hold the URL. **Does a personal Gmail address work?** Try it on your own data before you assume either way. Work addresses resolve more often, and we publish no hit-rate number because a number from our sample tells you nothing about your list. **Do I pay when the email resolves to nothing?** Yes, for the first call. That is the honest answer and it is the thing to model before a bulk run. Stop the chain there and you do not pay for the second. **Can I batch this?** There are bulk endpoints in the catalog for profile and company enrichment. **What is that response key again?** `data["LinkedIn Profile URL"]`, with spaces. Write the alias once in your client model and never think about it again. **Do I need a credit card?** No. 25 free credits on signup, which is enough to run the chain several times and see what your own addresses do.
- [Proxycurl alternative: the full endpoint map](/alternatives/proxycurl) — every endpoint, in one table - [Person Profile Endpoint replacement](/api/proxycurl-person-profile-endpoint) — call two of this chain, in full - [Company Profile Endpoint replacement](/api/proxycurl-company-profile-endpoint) — the company side, and the domain gap - [Employee Listing replacement](/api/proxycurl-employee-listing) — going the other way, company to people - [Email verification API](/api/email-verifier) — check the address before you send to it - [All endpoints](/api) — the full catalog - [Pricing](/pricing) — credit packages and tier rates
Run the chain against thirty addresses from your own list and count what resolves. **[Get 25 free credits](https://app.richapi.ai)**