Blog

Migrating off Proxycurl: mapping profile and company enrichment to two endpoints

Proxycurl shut down on 4 July 2025 after LinkedIn sued. Move your profile and company enrichment onto two endpoints, with before and after code.

The Triguna team 16 August 2026

Proxycurl shut down on 4 July 2025, after LinkedIn filed suit against its parent company in January 2025. If your code called its Person Profile or Company Profile endpoints, those two calls have a direct replacement. The other nineteen Proxycurl endpoints do not, and this post is honest about which is which before you write a line.

What Triguna replaces, and what it does not

Proxycurl exposed twenty-one endpoints. Triguna has two, plus a balance check:

GET /v1/people/profile      profile enrichment
GET /v1/companies/details   company enrichment
GET /v1/credits             credit balance

So this is a replacement for profile and company enrichment. It is not a replacement for search, employee listing, role lookup, jobs, or the school endpoint. There is no search or list path at all, which means enriching N entities is N calls, not one query that returns a page. If your Proxycurl usage was mostly the profile and company endpoints, this is a clean swap. If it leaned on search or listing, you need a different source for that part, and cutting it over to Triguna will quietly drop those workflows.

The endpoint mapping

ProxycurlTrigunaThe catch
Person Profile GET /proxycurl/api/v2/linkedinGET /v1/people/profilepass a slug in profile_id, not a full URL
Company Profile GET /proxycurl/api/linkedin/companyGET /v1/companies/detailspass the URL slug in company_id, not the display name
Person, Company, or Role Searchnoneno search or list path exists
Employee ListingnoneN entities is N direct calls
Jobs, School, and the restnoneout of scope for an enrichment API

Three things change in every request

The header, the parameter, and the base URL. Here is the person call in Node, before and after.

Before, on Proxycurl:

const res = await fetch(
  'https://nubela.co/proxycurl/api/v2/linkedin?url=' +
    encodeURIComponent('https://www.linkedin.com/in/williamhgates'),
  { headers: { Authorization: `Bearer ${process.env.PROXYCURL_KEY}` } },
);
const profile = await res.json();

After, on Triguna:

const res = await fetch(
  'https://api.triguna.ai/v1/people/profile?profile_id=williamhgates',
  { headers: { ApiKey: process.env.TRIGUNA_API_KEY } },
);
const profile = await res.json();

The auth header is ApiKey, not Authorization: Bearer. The identifier is a slug in profile_id, not a full linkedin.com URL in url. That is the whole swap for a profile lookup. The Profile API page shows the response shape.

The company call in Python, before and after:

import os, requests

# Before, on Proxycurl
res = requests.get(
    'https://nubela.co/proxycurl/api/linkedin/company',
    headers={'Authorization': f"Bearer {os.environ['PROXYCURL_KEY']}"},
    params={'url': 'https://www.linkedin.com/company/microsoft'},
)
company = res.json()
import os, requests

# After, on Triguna
res = requests.get(
    'https://api.triguna.ai/v1/companies/details',
    headers={'ApiKey': os.environ['TRIGUNA_API_KEY']},
    params={'company_id': 'microsoft'},
)
company = res.json()

company_id takes the URL slug, the path segment after /company/, so microsoft works and Microsoft Corporation returns a 404. The Company API page covers the fields, and the company records guide covers the two normalizations that catch people: platform_url is the page on the source platform while company_url is the company’s own site, and it is specialities, not specialties.

Provenance moves from the body to the headers

If your Proxycurl code read metadata out of the JSON body, that read has to move. Triguna keeps provenance in the response headers and never in the body, so a stored copy carries where it came from and when:

X-Data-Source: ...
X-Fetched-At: 2026-08-16T09:12:44Z
X-Data-Age-Seconds: 0
X-Credits-Charged: 1
X-RateLimit-Remaining: 9

There is no source field inside the JSON to parse. Record X-Data-Source and X-Fetched-At alongside the row. The full header list is in the caching reference.

Billing and retries differ enough to matter

Read this before you point production traffic at it, because the failure economics are not the same.

  • One credit per answered lookup, at $0.005 per credit. A cache hit costs one credit: same data, delivered faster. use_cache defaults to true.
  • A 404 costs one credit. Searching for an identifier that has no record is a real retrieval, so a missing profile bills the same as a found one. Never retry a 404, because a retried 404 costs a credit every time.
  • Free statuses, charged nothing: 400, 401, 402, 429, 502, 503. Retry only 429, 502, and 503, with backoff. The published rate limit is 10 requests per second.

The pricing page has the per-status table, and the error reference has the bodies.

The migration checklist

  1. Swap the auth header from Authorization: Bearer to ApiKey.
  2. Replace the full linkedin.com URL with the slug: profile_id for people, company_id for companies.
  3. Change the base URL and the two endpoint paths per the table above.
  4. Move any provenance read from the JSON body to the X- headers.
  5. Delete retry logic on 404. It now costs a credit each attempt.
  6. Find a different source for search, employee listing, jobs, and school. There is no equivalent here.
  7. Store entity_urn and company_id, not the editable public slug, so your joins survive a rename.

If you are wiring the first call into a signup flow rather than a backfill, the enrich on signup guide has the pattern that keeps a slow lookup off the critical path.

FAQ

Does Triguna replace Proxycurl’s search endpoints? No. It is two enrichment endpoints plus a credit balance check. There is no search, employee listing, jobs, or school lookup, and no bulk path.

Is there an SDK to drop in? No SDK, which matches Proxycurl’s plain REST. A GET with an ApiKey header is the entire client. Agents can read api.triguna.ai/llms.txt and the OpenAPI spec directly.

Do cache hits cost a credit? Yes. A cache hit is the same data delivered faster, and it bills one credit. During an outage a stored copy can come back marked stale_fallback instead of erroring.

Is there a bulk endpoint? No. N entities is N calls at up to 10 requests per second. Pace your backfill accordingly.

How do I get a key? Sign up for five credits, no card. The people who build this were Proxycurl customers before they were operators of an enrichment API, which is why the surface stays small enough to hold in your head.

Next

proxycurlmigrationenrichment

Start with one request

Free credits on signup, no card required. Usage-based pricing after that.