> ## Documentation Index
> Fetch the complete documentation index at: https://docs.minerva.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Person Search

> Find people using a natural-language audience description and return matching Minerva PIDs with contact-channel coverage stats

<div style={{ display: 'none' }} aria-hidden="false">
  ## Quick Answer

  **How do I find people with a natural-language query?** Use this endpoint to describe an audience in plain English (for example, "Software engineers in Denver with a professional email"). Minerva parses the query, runs it against the person dataset, and returns Minerva PIDs plus coverage statistics for available contact channels.

  **Common questions this endpoint answers:**

  * How do I search for people by description instead of filters?
  * How do I build an audience from a natural-language prompt?
  * How do I get Minerva PIDs for a persona or segment definition?
  * How do I know what percent of my audience has email, phone, or LinkedIn?
  * How do I preview an audience before exporting it?

  **What you need:** A short natural-language description of the audience (up to 500 characters).

  **What you get back:** A `search_id`, the list of matching Minerva PIDs (up to `size`), total match count, and contact-channel coverage stats for both the full match set and the returned page.

  **Common use cases:**

  * Turn a prompt like "CFOs at Series B fintechs in NYC" into a list of PIDs
  * Preview how large and how reachable an audience is before exporting
  * Feed results into `/enrich` or segments workflows
</div>

## Overview

The Person Search endpoint accepts a natural-language description of an audience and returns matching people from the Minerva dataset. Internally the service parses the prompt into structured filters, executes a search, and synchronously returns:

* A stable `search_id` you can retrieve again later
* Minerva PIDs for the returned page (up to `size`)
* The `total_count` of everyone matching the search (may exceed the returned page)
* Contact-channel coverage stats across the full match set (`total_contact_coverage`) and across the returned page (`results_contact_coverage`)

The call is synchronous. Typical latency is a few seconds depending on query complexity.

Usage is metered per organization per UTC day. See [Person Search Usage](/api-reference/person-search-usage) for limits and consumption.

## Writing good queries

The query is a natural-language audience *description* — not a lookup of a
specific named person. Best results come from describing the **attributes**
the people share. The parser handles these attribute categories well:

| Category                        | Examples                                                                                            |
| ------------------------------- | --------------------------------------------------------------------------------------------------- |
| **Role / job title**            | "engineers", "CFOs", "senior product managers", "VPs of engineering"                                |
| **Industry**                    | "fintech", "biotech", "SaaS", "venture capital"                                                     |
| **Company**                     | named companies ("Google", "Stripe"), groupings ("FAANG companies"), or stage ("Series B startups") |
| **Career history**              | "previously worked at Meta", "former Apple engineers", "left a Series A in 2024"                    |
| **Location**                    | cities, regions, or countries ("in NYC", "Bay Area", "based in Germany")                            |
| **Education**                   | school + major + level ("CS degrees from MIT", "Harvard MBAs")                                      |
| **Age**                         | ranges or descriptive ("30–40 years old", "in their 50s")                                           |
| **Income / wealth**             | "earning over 200k", "own homes worth over 1M"                                                      |
| **Contact-channel constraints** | "with a professional email on file", "have a verified LinkedIn"                                     |

Boolean logic (`and` / `or` / `not`) and nested groups work too — the
parser turns "MIT or Stanford CS grads" into the right OR group
automatically.

### Three example queries

Three patterns that cover most real audience definitions. Each maps to a
shape the parser handles cleanly out of the box.

<CodeGroup>
  ```bash 1. Simple — role + location + contact theme={null}
  curl --request POST \
    --url 'https://api.minerva.io/person-search/v0/search' \
    --header 'x-api-key: <api-key>' \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "Software engineers in Denver with a professional email on file",
      "size": 50
    }'
  ```

  ```bash 2. Moderate — role + company stage + industry + location theme={null}
  curl --request POST \
    --url 'https://api.minerva.io/person-search/v0/search' \
    --header 'x-api-key: <api-key>' \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "CFOs at Series B fintech startups in NYC",
      "size": 100
    }'
  ```

  ```bash 3. Advanced — multi-attribute with demographics + wealth theme={null}
  curl --request POST \
    --url 'https://api.minerva.io/person-search/v0/search' \
    --header 'x-api-key: <api-key>' \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "Engineers at FAANG companies with CS degrees from MIT or Stanford, 30-40 years old, who own homes worth over 1M",
      "size": 100
    }'
  ```
</CodeGroup>

### Tips

* **Describe attributes, not specific people.** A lookup of "John Doe at
  Acme Inc." is a PII match — use [`/v2/resolve`](/api-reference/resolve-v2)
  for that. Person Search is for audience definitions ("engineers at SaaS
  companies").
* **Be specific about role + company.** Adding "at Google" or "in fintech"
  narrows the result set materially vs. a bare role.
* **Combine 4–6 attributes for high-precision audiences.** Each additional
  filter sharpens the segment.
* **Add a contact-channel constraint** (e.g. "with a professional email")
  when you need to reach the audience — guarantees the returned PIDs are
  actually reachable on that channel.
* **Stay under 500 characters.** The endpoint validates `query` length
  (see [Request Body](#request-body)).

<RequestExample>
  ```bash Request Example theme={null}
  curl --request POST \
    --url 'https://api.minerva.io/person-search/v0/search' \
    --header 'x-api-key: <api-key>' \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "Software engineers in Denver with a professional email on file",
      "size": 50
    }'
  ```
</RequestExample>

## Request

### Headers

<ParamField header="x-api-key" type="string" required>
  Your API key for authentication.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

### Request Body

<ParamField body="query" type="string" required>
  Natural-language description of the people you want to find. Between 1 and 500 characters. See [Writing good queries](#writing-good-queries) for supported attribute categories and worked examples.
</ParamField>

<ParamField body="size" default="100" type="integer">
  Maximum number of Minerva PIDs to return in `results`. Must be between `1` and `100`. The `total_count` field still reflects the full match count even when `size` is smaller than the match set.
</ParamField>

### Request Example

```json theme={null}
{
  "query": "Software engineers in Denver with a professional email on file",
  "size": 50
}
```

## Response

### Success Response

The API returns a JSON object with the new `search_id`, the list of matching PIDs for this page, and coverage statistics for contact channels.

<ResponseField name="search_id" type="string">
  UUID identifying this search. Store it if you plan to fetch the same results later with [Get Person Search](/api-reference/person-search-get).
</ResponseField>

<ResponseField name="query" type="string">
  The normalized query associated with this search.
</ResponseField>

<ResponseField name="results" type="string[]">
  Array of Minerva PIDs for this page of results. Length is at most `size`. Each PID has the format `p-{hash}` and can be used with `/enrich`, segments, and other person-level endpoints.
</ResponseField>

<ResponseField name="total_count" type="integer">
  Total number of people matching the search. Can be larger than `result_count` if `size` is smaller than the match set.
</ResponseField>

<ResponseField name="result_count" type="integer">
  Number of PIDs actually returned in `results` for this response.
</ResponseField>

<ResponseField name="total_contact_coverage" type="object">
  Availability of contact channels across the full match set of `total_count` people. See [Coverage stats](#coverage-stats) below for object shape.
</ResponseField>

<ResponseField name="results_contact_coverage" type="object">
  Availability of contact channels across the `result_count` PIDs returned on this page. See [Coverage stats](#coverage-stats) below for object shape.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp (UTC) indicating when the search was created.
</ResponseField>

### Coverage stats

Both `total_contact_coverage` and `results_contact_coverage` share the same shape. Each channel entry reports how many people in the corresponding population have that channel on file, and what percentage that represents.

<ResponseField name="personal_email" type="object">
  Count and percent of people with a personal email on file.
</ResponseField>

<ResponseField name="professional_email" type="object">
  Count and percent of people with a professional email on file.
</ResponseField>

<ResponseField name="phone" type="object">
  Count and percent of people with a phone number on file.
</ResponseField>

<ResponseField name="linkedin" type="object">
  Count and percent of people with a LinkedIn profile on file.
</ResponseField>

Each channel object contains:

<ResponseField name="count" type="integer">
  Number of people in the population who have this contact channel on file.
</ResponseField>

<ResponseField name="percent" type="number">
  `count` divided by the size of the population, expressed as a percentage (`0.0`–`100.0`).
</ResponseField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "search_id": "550e8400-e29b-41d4-a716-446655440000",
    "query": "Software engineers in Denver with a professional email on file",
    "results": [
      "p-a1b2c3d4e5f6",
      "p-b2c3d4e5f6a7",
      "p-c3d4e5f6a7b8"
    ],
    "total_count": 1284,
    "result_count": 3,
    "total_contact_coverage": {
      "personal_email": { "count": 812, "percent": 63.24 },
      "professional_email": { "count": 1284, "percent": 100.0 },
      "phone": { "count": 497, "percent": 38.71 },
      "linkedin": { "count": 1110, "percent": 86.45 }
    },
    "results_contact_coverage": {
      "personal_email": { "count": 2, "percent": 66.67 },
      "professional_email": { "count": 3, "percent": 100.0 },
      "phone": { "count": 1, "percent": 33.33 },
      "linkedin": { "count": 3, "percent": 100.0 }
    },
    "created_at": "2026-04-24T15:02:11.482915+00:00"
  }
  ```

  ```json 400 theme={null}
  {
    "code": "no_filters_extracted",
    "message": "No usable filters could be derived from the query.",
    "api_request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
  ```

  ```json 401 theme={null}
  {
    "code": "unauthorized",
    "message": "Unauthorized",
    "api_request_id": "b2c3d4e5-f6a7-1234-bcde-f12345678901"
  }
  ```

  ```json 403 theme={null}
  {
    "code": "forbidden",
    "message": "Not authorized for this route or plan",
    "api_request_id": "c3d4e5f6-a7b8-2345-cdef-123456789012"
  }
  ```

  ```json 422 theme={null}
  {
    "code": "validation_error",
    "message": "Invalid request data",
    "api_request_id": "d4e5f6a7-b8c9-3456-def0-234567890123",
    "details": [
      {
        "loc": ["query"],
        "type": "string_too_long"
      }
    ]
  }
  ```

  ```json 429 theme={null}
  {
    "code": "rate_limit_reached",
    "message": "You reached your total request-rate limit, please contact help@minerva.io for help",
    "api_request_id": "e5f6a7b8-c9d0-4567-ef01-345678901234"
  }
  ```
</ResponseExample>

## Error Responses

### Common Errors

* `400` - **Bad Request**: The natural-language query could not be turned into any usable search filters. Rephrase the query with more concrete criteria (role, location, industry, seniority, etc.).
* `401` - **Unauthorized**: Invalid or missing API key
* `403` - **Forbidden**: The API key is valid but not authorized for this route or your plan tier
* `422` - **Unprocessable Entity**: Request validation failed. Examples: `query` missing or empty, `query` longer than 500 characters, `size` outside `1`-`100`, or body is not valid JSON.
* `429` - **Too Many Requests**: Rate limit or plan quota exceeded
* `500` - **Internal Server Error**: Unexpected server error

### Error Examples

**No usable filters extracted from the query:**

```json theme={null}
{
  "code": "no_filters_extracted",
  "message": "No usable filters could be derived from the query.",
  "api_request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

**Validation error (query too long):**

```json theme={null}
{
  "code": "validation_error",
  "message": "Invalid request data",
  "api_request_id": "b2c3d4e5-f6a7-1234-bcde-f12345678901",
  "details": [
    {
      "loc": ["query"],
      "type": "string_too_long"
    }
  ]
}
```

## Related Endpoints

* [Get Person Search](/api-reference/person-search-get) - Retrieve a previously created search by `search_id`
* [Async Person Search](/api-reference/person-search-job-submit) - Materialize a complete segment, inspect its size, then unlock and page PIDs
* [Person Search Usage](/api-reference/person-search-usage) - Daily delivery limits and consumption history
* [Contact Append](/api-reference/person-search-contact-append) - Async ABM contacts at target companies
* [Contact Prospect](/api-reference/person-search-contact-prospect) - Async contacts by NAICS + title
* [Contact Job Status](/api-reference/person-search-contact-job) - Poll contact-append / contact-prospect jobs
* [Contact Job Results](/api-reference/person-search-contact-job-results) - Unlock (bill) and page contact job results
* [Enrich v2](/api-reference/enrich-v2) - Fetch full profile data for the Minerva PIDs returned here
* [Add Members to Segment](/api-reference/segments-members-add) - Persist search results into a segment for tracking
