Data catalog · Search
Search profiles the way LinkedIn's own search works: fuzzy name matching, strict first and last name filters, current and past company, school, title, and LinkedIn's geo and industry IDs. Returns profile URLs, priced per result returned rather than per search.
Last updated September 22, 2026
`POST /profile_search` searches profiles on the dimensions LinkedIn itself organises people by: name, title, current and past employer, school, industry and geography. It returns profile URLs. **[Get 25 free credits — no card](https://app.richapi.ai)**
Nothing is required. An empty body is a valid request, which is a good reason to never send one. Name matching comes in two flavours and they behave differently. `search` is fuzzy. Give it a name and it tolerates spelling, middle names and ordering. `first_name` and `last_name` are strict filters. Use `search` when you are looking for a person whose name you heard; use the strict pair when you are matching against a record you already hold and a near-miss would be worse than nothing. Employment splits the same way. `current_company` and `past_company` take a company ID or LinkedIn URL, comma-separated for several. `keywords_company` and `keywords_school` are keyword matches on the name instead, which is what you want when you have a company's name as a string rather than its ID. `follower_of` filters to people following a given profile — the one filter here with no equivalent anywhere else in the catalog, and the way to build an audience list off a founder's following. Geography has a trap. `location` is free text from LinkedIn's autocomplete; `geo_id` is LinkedIn's own identifier and **overrides `location` when both are present**. Resolve the ID with the `geo_id_search` endpoint and pass `geo_id` when you care about the boundary being exact, because free-text locations match approximately and two runs can disagree. `industry_id` works the same way — an ID, not a label. `page` is **1-based**, defaulting to 1.
Two search endpoints in this catalog, and picking wrong costs you a rewrite. [People search](/api/people-search) is **account-shaped**. Array filters, company domains, headcount ranges, zero-based pagination. Use it when the question starts with the company: everyone with this title at companies of this size in this industry. `profile_search` is **person-shaped**. String filters, fuzzy names, schools, past employers, follower relationships, LinkedIn's own IDs, 1-based pagination. Use it when the question starts with the person: who is this, where else have they worked, who else went to this school or follows this founder. Different conventions on the same catalog is a wart, not a design. Read the pagination base before you write the loop.
This endpoint is **not** a multi-provider waterfall. Billing is on HTTP 2xx. A thin 2xx still bills. 4xx and 5xx bill nothing, on any endpoint. The miss-free rule on the email finder, phone finder and email verifier does not apply here.
**1. Get a key.** Sign up at [app.richapi.ai](https://app.richapi.ai). 25 free credits, no card. **2. Find a specific person by fuzzy name plus employer.** ```bash curl -X POST https://api.richapi.ai/api/v1/profile_search \ -H "x-api-key: $RICHAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "search": "Jane Okafor", "keywords_company": "Acme", "page": 1 }' ``` **3. Or filter a population by title, past employer and geography.** ```bash curl -X POST https://api.richapi.ai/api/v1/profile_search \ -H "x-api-key: $RICHAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Solutions Engineer", "past_company": "https://www.linkedin.com/company/example-co/", "geo_id": "", "page": 1 }' ``` **4. Read `elements`.** ```json { "elements": [ { "name": "example", "position": "example", "linkedinUrl": "example", "publicIdentifier": "example" } ], "pagination": {} } ``` `publicIdentifier` is the vanity slug. Store it alongside `linkedinUrl`, because it is the stabler of the two when someone edits their profile URL. **5. Enrich the ones worth enriching.** Pass `linkedinUrl` to [person enrichment](/api/person-enrichment) for a full record, or to [the email finder](/api/email-finder) for a work email. **6. Or search from an agent.** The [hosted MCP server](/integrations/claude) exposes `profile_search` as a tool: ``` Find solutions engineers who used to work at Example Co and are now in Berlin. One page only. ``` Say "one page only". On a per-result endpoint an agent left to paginate freely is an agent left to spend freely.
| Field | Type | What it is | |---|---|---| | `elements` | array | The matched profiles. **This array is also the billing unit** — `credits_per_result` is counted off its length. | | `elements[].name` | string | Display name. | | `elements[].linkedinUrl` | string | Profile URL. Feed this straight into [person enrichment](/api/person-enrichment). | | `elements[].position` | string | Headline or current position as LinkedIn renders it, not a normalised job title. | | `elements[].publicIdentifier` | string | The LinkedIn public slug. | | `pagination` | object | **Ships as `{}` in the spec.** Do not write a loop that depends on a key inside it. | Terminate pagination on an empty `elements` array, not on a `pagination` field nobody has confirmed exists. `position` is a headline string, so filtering on it client-side means string matching against whatever the person chose to write about themselves. Treat it as a hint, not a field.
`profile_search` bills `0.1 credits per result`, charged against the `elements` array. No flat call fee, no per-search fee, and no cost figure on this page means anything without a result count attached. | What you run | Results returned | Credits | |---|---|---| | A strict-name lookup that matches one person | 1 | `0.1 credits` | | One page of a title search | 25 | `2.5 credits` | | Ten pages of the same | 250 | `25 credits` | That shape makes precision cheap. A strict `first_name` + `last_name` + `current_company` lookup that returns exactly one person costs a tenth of a credit; the same question asked as a broad title sweep returns hundreds of rows and bills for every one. Filter hard, then enrich, rather than pulling wide and sorting later. Do not assume it inherits the zero-on-miss guarantee — that belongs to [the waterfall endpoints](/api/email-finder), and this is a different billing path. Credit rates are tiered by package on [the pricing page](/pricing).
**Matching a record you already have to a real profile.** You hold a name and an employer from a form fill or a conference list. Strict `first_name` + `last_name` + `keywords_company` returns one row for a tenth of a credit, and that row gives you the profile URL that keys everything else. **Alumni and past-employer plays.** `past_company` and `school` are the filters behind the two outbound angles that still get replies: people who worked somewhere you have a reference customer, and people who went where your founder went. Neither is expressible in a domain-and-headcount filter set. **Audience lists from a following.** `follower_of` gives you the people following a specific profile. For a founder-led category that is a sharper definition of "interested in this space" than any industry code, and there is no other endpoint here that produces it.
- [B2B people search API](/api/people-search) — the account-shaped search, array filters and 0-based pages - [Person enrichment API](/api/person-enrichment) — the full record behind a result URL - [Email to LinkedIn URL API](/api/email-to-linkedin) — the reverse route, when you start from an address - [Email finder API](/api/email-finder) — a work email from a profile URL, zero-billed on a miss - [All endpoints](/api) — the full catalog - [Pricing](/pricing) — credit packages and tier rates
**What am I charged for?** Results returned, measured on the `elements` array, on 2xx responses. There is no per-search fee and no flat call fee. Errors bill nothing. This is not a waterfall endpoint, so the zero-on-miss rule does not apply. **What is the difference between `search` and `first_name`/`last_name`?** `search` matches fuzzily and tolerates variants; `first_name` and `last_name` are strict. Fuzzy finds people whose names you are unsure of and returns more rows, which on per-result pricing also costs more. **Why is my `location` filter being ignored?** Because you also sent `geo_id`, which overrides it. Send one or the other. Resolve `geo_id` through `geo_id_search` rather than guessing an ID. **How is this different from people search?** Different shape, not a different tier. [People search](/api/people-search) filters by company attributes with arrays and 0-based pages; this filters by person attributes with strings and 1-based pages. Pick by whether your question starts with the company or the person. **Can I page through everything that matches?** You can, and on a per-result endpoint that is the expensive way to use it. Narrow the filters until the first page is mostly people you would actually contact. **Do results include emails or phone numbers?** No. You get `name`, `position`, `linkedinUrl` and `publicIdentifier`. Contact details are separate calls: [email finder](/api/email-finder) and [phone finder](/api/phone-finder), both of which bill zero when nobody has the answer. **Can I call this from Claude or Cursor (MCP)?** Yes, the hosted MCP server exposes `profile_search` as a tool with no install. Agents do well with the fuzzy `search` filter, since resolving a half-remembered name is the kind of problem they are given; tell them how many pages they may pull. **Do I need a credit card?** No. 25 free credits on signup, no card.
Run your first profile search in two minutes. **[Get 25 free credits](https://app.richapi.ai)**