---
title: "Find people from public data"
method: POST
path: "/public/search"
tags: ["public"]
---

# Find people from public data

`POST /public/search`

Search for people through an anonymous public web search; no connected account is needed. Results are public data, never signed-in LinkedIn details (connection degree, who engaged with a post, whether someone is reachable by message). Found profiles are saved as contacts and render as a people list; narrate a one-line count. limit (1-300, default 15) is how many profiles ONE call targets: a specific number sets it, a volume word with no ceiling raises it toward 100-300. Pass every plausible title, seniority, or city as an array on the SAME filter field in ONE call. more:true continues the SAME filters past people already found. exhaustion:"axes-exhausted" means every angle was tried: suggest a real change, do not call more:true again. exhaustion:"batch-failed" is a temporary vendor hiccup. A signal that would show in a headline (years in role, a school, a tool, freelance) belongs in filters.keywords, not a visit-first pass.

## Request body

- object
  - `filters` object, required — Search facets. Provide at least one of title, location, company, industry, or keywords.
    - `title` union — Role or job title facet. One value or a list of alternatives.
      - string
      - string[]
    - `location` union — City, region, or country facet. One value or a list of alternatives.
      - string
      - string[]
    - `company` union — Company name facet. One value or a list of alternatives.
      - string
      - string[]
    - `industry` union — Industry facet. One value or a list of alternatives.
      - string
      - string[]
    - `seniority` union — Seniority facet. One value or a list of alternatives.
      - string
      - string[]
    - `keywords` string — Free-text keywords appended to the search query.
    - `countryCode` string — ISO alpha-2 country code used to scope results to one country.
  - `limit` integer — Target number of new profiles to return.
  - `save` boolean — When true, found profiles are saved to your contacts.
  - `chain` object — Optional two-step mode for DISCOVERING companies the caller does not already know by name (an industry, sector, or location ask): first find companies matching filters.industry/filters.location, then search this role inside each. Do NOT set this when specific companies are already named — pass them directly as filters.company instead (no chain needed): that searches those exact companies' roles directly, at lower cost and without a discovery step that can fail.
    - `role` string, required — Role searched inside each matched company.
    - `companiesLimit` integer — How many companies to search inside.
  - `more` boolean — Continue past the people an earlier search on these same filters already returned, instead of returning them again.

## Response `200`

Found and enriched people

- object
  - `source` 'public', required
  - `provider` string, required — The search provider that answered.
  - `query` string, required — The exact query sent, so a thin result set can be read rather than guessed at.
  - `existingCount` integer — People this search matched who were ALREADY in your contacts. They are linked into the target pool but not re-listed in `contacts`, because they were not re-fetched. Absent when there were none. Without it a caller reads a matched contact as a missed one.
  - `urlsFound` integer, required — Total profile URLs found.
  - `newUrls` integer, required — URLs not already in your contacts.
  - `hydrated` integer, required — Profiles fetched and enriched in this call.
  - `searches` integer — How many searches it took to find these people.
  - `searchesFailed` integer — Searches that did not reach the provider. When this is high the result says nothing about the market.
  - `moreAvailable` boolean — Whether asking again with more can still reach people you have not seen.
  - `exhaustion` 'more-available' | 'axes-exhausted' | 'batch-failed' — Finer than moreAvailable: 'axes-exhausted' means every search angle for this audience has genuinely been tried, calling again with more will not find anybody new. 'batch-failed' means this attempt could not reach the search provider, worth retrying. 'more-available' is the ordinary case.
  - `axesTried` object[] — What was actually tried, broken down by search angle, against what each angle could still do.
    - `axis` string, required — Which search angle this row covers, e.g. title, city, school, tool.
    - `queriesPlanned` integer, required — How many searches this angle could run in total.
    - `attempted` integer, required — How many of those have actually run so far.
    - `yielded` integer, required — New people this angle has contributed.
    - `exhausted` boolean, required — Whether this angle has nothing left to try.
  - `axesAvailable` object[] — Every search angle this audience supports and how many queries each could run — the full potential, whether or not it has been searched yet. Free to compute, no extra searches run to produce it.
    - `axis` string, required — A search angle this audience supports, e.g. title, city, school, tool.
    - `queries` integer, required — How many searches that angle could run in total.
  - `countryRiskNote` object — A non-blocking caveat about this market, e.g. a country whose profiles are hard to tell apart from a bigger anglophone one. Never a reason to withhold or shrink the result — surface it as a caution alongside the real count, never in place of one.
    - `risk` 'low' | 'medium' | 'high', required — How likely this country/title combination is to return false-positive matches from elsewhere.
    - `reason` string, required — Why, in plain language.
  - `rateLimited` integer — Profiles found but deferred because the scraping pool is busy.
  - `note` string — Explanation when part of the result set was deferred.
  - `contacts` object[], required
    - `name` string, nullable, required
    - `headline` string, nullable, required
    - `company` string, nullable, required
    - `location` string, nullable, required
    - `profileUrl` string, nullable, required
    - `completeness` string, required
    - `summary` string — The profile's About text, when the public page carried one.
    - `connections` integer — Connection count, when the public page carried one.
    - `imageUrl` string — The profile's display photo (from the public page's og:image), when the public page carried one. A raw LinkedIn CDN URL — may expire over time, unlike a re-hosted avatar.
    - `education` string — School name from the public page, when present.
    - `followers` integer — Public follower count from the preview page, when present. Not a login-only exact count.
    - `badge` string — Public profile badge from the preview page, when present.
    - `languages` string[] — Languages listed on the public preview page, when present.
    - `positions` object[] — Visible employers from the public preview page, when present.
      - `companyName` string, required
      - `title` string, nullable, required
      - `companyUrl` string, nullable, required
      - `startDate` object, nullable, required
        - `year` number, required
      - `endDate` object, nullable, required
        - `year` number, required
      - `isCurrent` boolean, required
    - `educations` object[] — Visible schools from the public preview page, when present.
      - `schoolName` string, required
      - `schoolUrl` string, nullable, required
      - `startDate` object, nullable, required
        - `year` number, required
      - `endDate` object, nullable, required
        - `year` number, required
    - `recentPosts` object[] — A sample of the person's own public feed posts from the preview page, when present. Search returns at most three; the saved contact keeps the fuller public list.
      - `url` string, nullable, required
      - `text` string, nullable, required
      - `likes` number, nullable, required
      - `datePublished` string, nullable, required

## Other responses

- `400` — The server cannot or will not process the request due to something that is perceived to be a client error.
- `401` — Although HTTP specifies "unauthorized", this response means "unauthenticated". Authenticate to continue. NOTE: 401 is also returned with code "linkedin_not_connected" when the caller IS authenticated but has no connected LinkedIn account — connect LinkedIn (not re-authenticate) to continue.
- `403` — The client does not have access rights to the content.
- `404` — The server cannot find the requested resource.
- `409` — The request conflicts with the current state of the server.
- `410` — The requested content has been permanently deleted from the server.
- `422` — The request was well-formed but was unable to be followed due to semantic errors.
- `429` — Rate limit exceeded. Read error.retryAfter for the wait time in seconds.
- `500` — The server encountered a situation it does not know how to handle.
- `502` — LinkedIn returned a server error or the proxy connection failed. Retry after a few seconds.
- `503` — Proxy capacity temporarily exceeded. Retry after a few seconds.

---

[API](https://skmtc.net/berea/apis/bereach-api.md) · [All operations](https://skmtc.net/berea/apis/bereach-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/berea/bereach-api/revisions/a1e86af2406d/schema)
