Skip to main content
Glama

Exa Find People

exa_find_people

Use AFTER companies have been identified (company-find step in the same agent, user named a target, CRM lookup, etc.) to surface specific decision-makers. Results are always LinkedIn URLs.

Before composing a search, call get_skill_guide('exa_find_people') — it owns the query vs criteria split that decides whether results come back useful or noisy (exclusions in query backfire; they belong in criteria).

Every person a search returns is checked against its criteria, one at a time and whichever provider served it, and results holds only the matches, most senior first; non-matches come back in rejected as {name, reason} with no URL. Put exclusions in criteria.

Pass company_domain or company_linkedin_url whenever you have one. A search carrying either is answered by a people database that filters on that identifier, so it cannot return someone at a same-named company. Without one, the search falls to a name-matched provider, and the check is what keeps a same-named company's staff out, reading each person's current and past roles against criteria.

Both providers read a profile snapshot refreshed periodically, not live reality, so someone who changed jobs since the last crawl can still pass.

Accepts a list of ExaPeopleSearch — pass one for a single-company lookup, or N for a batch, fanned out in parallel (max 5 concurrent, since the name-matched provider has shown 5xx instability under bursty load). Each search routes on its own, so one batch can be served by both providers. Each call retries once on 5xx inside the HTTP client.

An identifier-pinned search matching more people than can be returned at once comes back in its own slot as {error, query} asking you to narrow by titles (and locations, at a multinational); the rest of the batch is unaffected. Re-send that one search narrowed.

Costs 0.5 Sliq credits per search that returns without error, whichever provider served it, so an empty result set still charges (the call was made). Free on BYO Exa (a connected Exa key). Soft-fails on insufficient credits: the data is returned and the charge is capped at the remaining balance. List of per-search result dicts in input order. Each is either {results: [...], rejected: [...]} on success, or {error: '...', query: '...'} on failure (after the in-client retry). results entries are {name, url, summary, verdict, verdict_reason, seniority_level}, most senior first, with url always a LinkedIn profile URL, summary holding name, current_title, current_company, location and tenure, verdict 'qualified', or 'unclear' when the profile says too little to judge, and seniority_level one of c_level, vp, director, senior_manager, manager, ic, unknown (both null when the check couldn't run). rejected entries are {name, reason} only.

On insufficient Sliq credits the batch is refused before running: it raises InsufficientCreditsError rather than returning per-search slots.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idNoOptional — a specific agent to save the checked people into. Omit it to use the running agent, which is the usual case. Only a throwaway lookup outside any agent returns results without saving.
searchesYesList of `ExaPeopleSearch` objects. Each has its own `query` + `criteria`. For a single-company lookup, pass a list of one.
list_nameNoShort kebab slug naming the Output-tab list bucket (e.g. 'acme-execs'). When this runs in an agent, the people the check judged are saved and linked as `agent_search_results` person rows automatically, each with its verdict (the rejected ones hidden from the list's default view) — deduped by profile, re-runs update in place; `list_name` names their list, and absent it they land in the 'default' list. Query them by reference with `query_search_results` instead of re-typing URLs from `results`. The `results` return is unchanged either way.
num_resultsNoCandidates Exa retrieves and ranks per search before the check runs. Default 10; max 100. Matches cluster at the top of Exa's ranking, so 10 usually holds the right people for a well-formed query; the returned `results` are shorter after the check. If `results` is thin and `rejected` is long, reword the query (the reject reasons say whether and how) rather than raising this — the tail ranks are lower-relevance anyway.
on_behalf_ofNoAn agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedInput schema / properties / on_behalf_of
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
      +}
  2. Changed1 schema field changed
    • changedInput schema / properties / searches / items / properties / company_domain / description
      Previous value: -"The company's website domain, e.g. 'acme.com'. Pass it whenever you have it — a domain names one employer, where a company name matches every company written that way, and same-name companies are the main source of wrong-company results. A company row's `identifier` holds one; pass it as stored, whether that is a bare host or a full URL."New value: +"The company's website domain, e.g. 'acme.com'. Pass it whenever you have it — a domain names one employer, where a company name matches every company written that way, and same-name companies are the main source of wrong-company results. A company row's `identifier` holds its domain; pass it as stored."
  3. Changed1 schema field changed
    • removedInput schema / properties / persist
      Removed value: -{
      -  "default": true,
      -  "description": "Default True — in an agent, checked people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list.",
      -  "type": "boolean"
      -}
  4. Changed13 schema fields changed
    • changedInput schema / properties / agent_id / description
      Previous value: -"Optional — a specific agent to save the matched people into.\nOmit it to use the running agent, which is the usual case. Only a\nthrowaway lookup outside any agent returns results without saving."New value: +"Optional — a specific agent to save the checked people into.\nOmit it to use the running agent, which is the usual case. Only a\nthrowaway lookup outside any agent returns results without saving."
    • changedInput schema / properties / list_name / description
      Previous value: -"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in an agent, matched people are saved\nand linked as `agent_search_results` person rows automatically —\ndeduped by profile, re-runs update in place; `list_name` names\ntheir list, and absent it they land in the 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."New value: +"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in an agent, the people the check\njudged are saved and linked as `agent_search_results` person rows\nautomatically, each with its verdict (the rejected ones hidden from\nthe list's default view) — deduped by profile, re-runs update in\nplace; `list_name` names their list, and absent it they land in\nthe 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."
    • changedInput schema / properties / num_results / description
      Previous value: -"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway."New value: +"Candidates Exa retrieves and ranks per search before\nthe check runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter the check. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway."
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list."New value: +"Default True — in an agent, checked people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list."
    • changedInput schema / properties / searches / description
      Previous value: -"List of `ExaPeopleSearch` objects. Each has its own\n`query` + optional `system_prompt`. For a single-company\nlookup, pass a list of one."New value: +"List of `ExaPeopleSearch` objects. Each has its own\n`query` + `criteria`. For a single-company lookup, pass a list\nof one."
    • changedInput schema / properties / searches / items / description
      Previous value: -"One people search: who to find, and at which company.\n\nMultiple of these can be batched into a single `exa_find_people`\ncall — the tool fans them out in parallel and returns results in\ninput order. Each search is independent (its own query +\nsystem_prompt); the shared `num_results` is passed as a kwarg on the\nparent tool call."New value: +"One people search: who to find, and at which company.\n\nMultiple of these can be batched into a single `exa_find_people`\ncall — the tool fans them out in parallel and returns results in\ninput order. Each search is independent (its own query +\ncriteria); the shared `num_results` is passed as a kwarg on the\nparent tool call."
    • changedInput schema / properties / searches / items / properties / company / description
      Previous value: -"The company this search is aimed at, by name, on its own. Anyone placed at a differently-named employer is then dropped before you see them — a founder of 'Acmely' coming back for an 'Acme' search. The company name inside `query` / `system_prompt` steers ranking; this is what filters. Required, with no default, so a search running unfiltered is a decision rather than an omission: pass null for a cross-company search ('pricing leaders across chemical distributors'), and for an alumni search, where the person's current employer is a different company by definition."New value: +"The company this search is aimed at, by name, on its own; null for a cross-company search ('pricing leaders across chemical distributors') or an alumni search. When `company_domain` / `company_linkedin_url` pins the search, the check is also told this name and that identifier as the employer searched for, so it can tell that employer's staff from a same-named company's. Requiring that they work there now is still `criteria`'s job."
    • addedInput schema / properties / searches / items / properties / criteria
      Added value: +{
      +  "description": "What a match is for this search, in plain words: the requirements a person must meet, led by the employer and that they work there now (or the one person you're after), then any role, seniority, region or exclusion that decides it. The right place for exclusions. Each person returned is checked against all of it as requirements, so leave out a preference that only orders results (VPs ahead of Directors, the founder ahead of a first sales hire) and name everyone you'd accept. Example: 'Currently works at Microsoft, not a former employee. Product leader. Not a growth marketer.'",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • changedInput schema / properties / searches / items / properties / locations / description
      Previous value: -"Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of the results — naming a place in `query` or `system_prompt` does not filter a search pinned by `company_domain` / `company_linkedin_url`."New value: +"Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of what a search pinned by `company_domain` / `company_linkedin_url` fetches: `criteria` only rejects from what was fetched, and `query` doesn't reach that search at all."
    • changedInput schema / properties / searches / items / properties / query / description
      Previous value: -"POSITIVE-FORM description of who you want, including the company name. Examples: 'VP of Product at Microsoft', 'senior marketing leaders at Exaforce'. Do NOT include exclusions here — vector retrieval has no concept of 'not', so naming an excluded term biases the ranker TOWARDS it."New value: +"POSITIVE-FORM description of who you want, including the company name. Examples: 'VP of Product at Microsoft', 'senior marketing leaders at Exaforce'. On a search not pinned by `company_domain` / `company_linkedin_url`, include the region the ask names ('VP of Product at Microsoft in the US'): that search has no location filter, so `locations` and `criteria` only reject from the people `query` retrieves. Do NOT include exclusions here — vector retrieval has no concept of 'not', so naming an excluded term biases the ranker TOWARDS it."
    • removedInput schema / properties / searches / items / properties / system_prompt
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "string"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "Optional ranker strategy hint — the right place for preferences and exclusions. Example: 'MUST currently work at Microsoft. Prioritize VP+ over Director+. Exclude growth marketers.'"
      -}
    • changedInput schema / properties / searches / items / properties / titles / description
      Previous value: -"Role keywords, e.g. ['Operations', 'Training']. Each is matched as a case-insensitive substring of the person's job title and the list is OR-ed, so short function or seniority words are what match: 'Marketing', 'Revenue', 'Director', 'Chief'. A full title phrase matches only where it appears verbatim, which misses every company that words that title differently or abbreviates it — so always include the broad function word on its own ('Marketing') before adding specific titles, and pass both spellings of a rank ('VP' and 'Vice President' match different people). Entries are OR-ed, so a title that matches nobody costs nothing. Narrowing also decides WHICH people come back at a big employer: results are unordered, so without it you get an arbitrary slice of everyone there rather than the roles you asked for."New value: +"Role keywords, e.g. ['Operations', 'Training']. Each is matched as a case-insensitive substring of the person's job title and the list is OR-ed, so short function or seniority words are what match: 'Marketing', 'Revenue', 'Director', 'Chief'. A full title phrase matches only where it appears verbatim, which misses every company that words that title differently or abbreviates it — so always include the broad function word on its own ('Marketing') before adding specific titles, and pass both spellings of a rank ('VP' and 'Vice President' match different people). Entries are OR-ed, so a title that matches nobody costs nothing. Narrowing also decides WHICH people come back at a big employer: the fetch is unordered, so without it you get an arbitrary slice of everyone there rather than the roles you asked for."
    • changedInput schema / properties / searches / items / required
      Previous value: -[
      -  "query",
      -  "company"
      -]New value: +[
      +  "query",
      +  "criteria",
      +  "company"
      +]
  5. Changed1 schema field changed
    • changedInput schema / properties / num_results / description
      Previous value: -"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say how) rather than\nraising this — the tail ranks are lower-relevance anyway."New value: +"Candidates Exa retrieves and ranks per search before\nthe match filter runs. Default 10; max 100. Matches cluster at\nthe top of Exa's ranking, so 10 usually holds the right people\nfor a well-formed query; the returned `results` are shorter\nafter filtering. If `results` is thin and `rejected` is long,\nreword the query (the reject reasons say whether and how)\nrather than raising this — the tail ranks are lower-relevance\nanyway."
  6. Changed2 schema fields changed
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False to return\nresults without saving, for a flow that folds these people into\nanother row instead — e.g. a company-find that nests them under each\ncompany row via `record_search_results`, where a standalone person\nlist would duplicate people already shown under their company."New value: +"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False only for a\nread-only lookup the user doesn't want on a list."
    • addedInput schema / properties / searches / items / properties / locations
      Added value: +{
      +  "anyOf": [
      +    {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Places the person must be based in, by country, state or city name, e.g. ['United States'] or ['Texas', 'Colorado']. Entries are OR-ed, and the filter applies whichever provider serves the search. Spell states out: a two-letter code is read as a country, so 'CT' does not mean Connecticut. At a multinational this is what keeps overseas staff out of the results — naming a place in `query` or `system_prompt` does not filter a search pinned by `company_domain` / `company_linkedin_url`."
      +}
  7. Changed4 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Optional — a specific agent to save the matched people into.\nOmit it to use the running agent, which is the usual case. Only a\nthrowaway lookup outside any agent returns results without saving."
      +}
    • changedInput schema / properties / list_name / description
      Previous value: -"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in a task, matched people are saved\nand linked as `agent_search_results` person rows automatically —\ndeduped by profile, re-runs update in place; `list_name` names\ntheir list, and absent it they land in the 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."New value: +"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in an agent, matched people are saved\nand linked as `agent_search_results` person rows automatically —\ndeduped by profile, re-runs update in place; `list_name` names\ntheir list, and absent it they land in the 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."
    • changedInput schema / properties / persist / description
      Previous value: -"Default True — in a task, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False to return\nresults without saving, for a flow that folds these people into\nanother row instead — e.g. a company-find that nests them under each\ncompany row via `record_search_results`, where a standalone person\nlist would duplicate people already shown under their company."New value: +"Default True — in an agent, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False to return\nresults without saving, for a flow that folds these people into\nanother row instead — e.g. a company-find that nests them under each\ncompany row via `record_search_results`, where a standalone person\nlist would duplicate people already shown under their company."
    • removedInput schema / properties / task_id
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "integer"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "Optional — a specific task to save the matched people into.\nOmit it to use the running task, which is the usual case. Only a\nthrowaway lookup outside any task returns results without saving."
      -}
  8. Changed3 schema fields changed
    • changedInput schema / properties / list_name / description
      Previous value: -"Short kebab slug naming the list bucket (e.g.\n'acme-execs'). Pass together with `task_id`. When set, the\nmatched people are also saved as `agent_search_results` person\nrows under this slug (deduped by profile, re-runs update in\nplace) — query them by reference with `query_search_results`\ninstead of re-typing URLs from `results`. The `results` return\nis unchanged either way."New value: +"Short kebab slug naming the Output-tab list bucket (e.g.\n'acme-execs'). When this runs in a task, matched people are saved\nand linked as `agent_search_results` person rows automatically —\ndeduped by profile, re-runs update in place; `list_name` names\ntheir list, and absent it they land in the 'default' list. Query\nthem by reference with `query_search_results` instead of re-typing\nURLs from `results`. The `results` return is unchanged either way."
    • addedInput schema / properties / persist
      Added value: +{
      +  "default": true,
      +  "description": "Default True — in a task, matched people are saved and linked\nas person rows automatically (see `list_name`). Pass False to return\nresults without saving, for a flow that folds these people into\nanother row instead — e.g. a company-find that nests them under each\ncompany row via `record_search_results`, where a standalone person\nlist would duplicate people already shown under their company.",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / task_id / description
      Previous value: -"Pass together with `list_name` to save every matched\nperson into this task's Output-tab list. Omit both for a\nthrowaway lookup."New value: +"Optional — a specific task to save the matched people into.\nOmit it to use the running task, which is the usual case. Only a\nthrowaway lookup outside any task returns results without saving."
  9. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far exceeds the annotation bar (readOnlyHint=false, destructiveHint=false). It discloses provider routing, per-provider filtering semantics, snapshot staleness, batch fan-out with a 5-concurrency cap, in-client retry on 5xx, per-search cost (0.5 credits, charged even on empty results), BYO-Exa free tier, and soft-fail/capped charging on insufficient credits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and structured via summary/returns tags, but it is very long and repeats the exclusions-in-criteria point across two paragraphs. Dense and useful, yet slightly bloated for a tool description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex, high-stakes tool with no output schema, the embedded <returns> block fully documents the per-search result/rejected/error shapes, field semantics, and the insufficient-credits exception. Nothing an agent needs to call or interpret it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3, but the description adds real meaning beyond it: the query-vs-criteria split, 'put exclusions in criteria', the rationale for passing company_domain/company_linkedin_url, and the single-vs-batch framing of the searches list. It goes beyond repeating the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Find), resource (people on LinkedIn), and scope (one or more company searches), and positions itself precisely in a workflow ('Use AFTER companies have been identified'). An agent can tell this apart from the upstream company-find step without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use it (after companies identified: company-find step, user-named target, CRM lookup) and adds a mandatory prerequisite, calling get_skill_guide('exa_find_people') for the query-vs-criteria split. This is actionable routing guidance rather than implied context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources