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

# Search People

Search the DealMachine people/contacts database using filters. Control which fields are returned, how results are paginated and sorted, and optionally include property filters to find people connected to specific types of properties.

When property filters are included, set `property_match` to define the person-to-property relationship. Matching properties are nested in a `properties` array on each person.

## Body Parameters

<ParamField body="locations" type="array">
  Array of [location objects](/concepts/locations) defining where to search. Required unless filters or protocol filters are provided (max 15). Locations use **OR** logic — results matching any location are included.

  <Expandable title="Location object properties">
    <ParamField body="type" type="string" required>
      Location type: `state`, `county`, `city`, `zip_code`, `radius`, or `polygon`.
    </ParamField>

    <ParamField body="code" type="string">
      Location identifier. Required for `state` (2-letter abbreviation, e.g., `"TX"`), `county` (5-digit FIPS code, e.g., `"29189"`), `city` (place ID, e.g., `"7333"`), and `zip_code` (5-digit ZIP, e.g., `"63101"`).
    </ParamField>

    <ParamField body="latitude" type="number">
      Center point latitude. Required for `radius`.
    </ParamField>

    <ParamField body="longitude" type="number">
      Center point longitude. Required for `radius`.
    </ParamField>

    <ParamField body="radius_miles" type="number">
      Search radius in miles. Required for `radius`.
    </ParamField>

    <ParamField body="coordinates" type="array">
      Array of `[longitude, latitude]` pairs defining the boundary. Required for `polygon` (minimum 3 points).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="filters" type="array">
  Array of [filter objects](/concepts/filter-values). Required unless locations or protocol filters are provided. You can mix people filters (`source_type=people`) and property filters (`source_type=properties`).

  <Expandable title="Filter object properties">
    <ParamField body="filter_id" type="string" required>
      The filter slug from the [List Filters](/api-reference/filters/list-filters) endpoint (e.g., `has_phone`, `estimated_value`).
    </ParamField>

    <ParamField body="operator" type="string">
      One of the filter's `allowed_operators`. See [Filter Values](/concepts/filter-values) for all operators by type. **Optional for BOOLEAN filters** — automatically defaults to `is_boolean`.
    </ParamField>

    <ParamField body="value" type="any" required>
      The filter value. Shape depends on the operator — can be a number, string, boolean, array, or object. See [Filter Values](/concepts/filter-values).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="include_lists" type="object">
  Restrict results to people list IDs, for example `{ "people_list_ids": [123] }`.
</ParamField>

<ParamField body="exclude_lists" type="object">
  Exclude people list IDs, for example `{ "people_list_ids": [456] }`.
</ParamField>

<ParamField body="exclude_previously_exported" type="boolean | object">
  Exclude people already exported by your organization. Pass `true` for defaults or a full Query
  Builder export exclusion object.
</ParamField>

<ParamField body="bigquery_data_environment" type="integer">
  Query Builder dataset environment: `1` production, `2` staging, `3` development.
</ParamField>

<ParamField body="fields" type="string[]">
  [Field IDs](/concepts/fields) to include in results. Can include both people and property fields.
  Omit or pass an empty array for the default set. Property fields appear in the nested `properties`
  array when `property_match` is set.
</ParamField>

<ParamField body="property_match" type="string">
  **Required** when property filters are present. Defines the person-to-property relationship. Ignored when only people filters are used.

  Options: `owner`, `resident`, `renter`
</ParamField>

<ParamField body="page" type="integer" default={1}>
  Page number (min: `1`).
</ParamField>

<ParamField body="per_page" type="integer" default={25}>
  Results per page (min: `1`, max: `250`).
</ParamField>

<ParamField body="sort" type="array">
  Array of sort objects for ordering results. See [Pagination & Sorting](/concepts/pagination#sorting).

  <Expandable title="Sort object properties">
    <ParamField body="field_id" type="string" required>
      The field to sort by.
    </ParamField>

    <ParamField body="direction" type="string" required>
      `asc` (ascending) or `desc` (descending).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="estimate_cost" type="boolean" default={false}>
  When `true`, returns a credit cost estimate **without returning data or consuming credits**. See
  [Cost Estimate](#cost-estimate).
</ParamField>

***

<RequestExample>
  ```bash Search theme={null}
  curl -X POST "https://api.v2.dealmachine.com/v1/people/search" \
    -H "Authorization: Bearer dm_sk_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{
      "locations": [
        { "type": "state", "code": "TX" }
      ],
      "property_match": "owner",
      "filters": [
        {
          "filter_id": "estimated_value",
          "operator": "greater_than",
          "value": 500000
        },
        {
          "filter_id": "has_phone",
          "value": true
        }
      ],
      "fields": [
        "full_name",
        "phones",
        "emails",
        "estimated_value",
        "equity_percent"
      ],
      "page": 1,
      "per_page": 25,
      "sort": [
        { "field_id": "full_name", "direction": "asc" }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "data": [
      {
        "dm_person_id": "per_x1y2z3",
        "full_name": "John Smith",
        "first_name": "John",
        "last_name": "Smith",
        "phones": [{ "number": "5125551234", "type": "wireless", "do_not_call": false }],
        "emails": [{ "address": "john.smith@example.com" }],
        "address": "456 Oak Lane",
        "city": "Austin",
        "state": "TX",
        "zip": "78701",
        "full_address": "456 Oak Lane, Austin, TX 78701",
        "properties": [
          {
            "dm_property_id": "prop_a1b2c3",
            "full_address": "1200 Barton Springs Rd, Austin, TX 78704",
            "address": "1200 Barton Springs Rd",
            "city": "Austin",
            "state": "TX",
            "zip": "78704",
            "estimated_value": 875000,
            "equity_percent": 72
          }
        ]
      }
    ],
    "totals": {
      "people": 412,
      "properties": 387
    },
    "credits": {
      "used": 25,
      "properties": 0,
      "people": 25,
      "deduplicated": 0
    },
    "pagination": {
      "page": 1,
      "per_page": 25,
      "total_results": 412,
      "total_pages": 17,
      "has_next_page": true,
      "has_previous_page": false
    }
  }
  ```
</ResponseExample>

## Response

Every search response contains four top-level keys: `data`, `totals`, `credits`, and `pagination`. See [Credits](/concepts/credits) for full details on how credits work.

### `data`

An array of person objects. Each person includes the [always-included people fields](/concepts/response-format#people) — name, phones, emails, and primary address — plus any fields you requested. Credits are consumed per entity.

When `property_match` is set and property filters are present, each person also includes a `properties` array with matching properties.

```json theme={null}
{
  "dm_person_id": "per_x1y2z3",
  "full_name": "John Smith",
  "first_name": "John",
  "last_name": "Smith",
  "middle_initial": null,
  "suffix": null,
  "phones": [
    { "number": "5125551234", "type": "wireless", "do_not_call": false },
    { "number": "5125559999", "type": "landline", "do_not_call": false }
  ],
  "emails": [{ "address": "john.smith@example.com" }],
  "address": "456 Oak Lane",
  "city": "Austin",
  "state": "TX",
  "zip": "78701",
  "full_address": "456 Oak Lane, Austin, TX 78701",
  "properties": [
    {
      "dm_property_id": "prop_a1b2c3",
      "full_address": "1200 Barton Springs Rd, Austin, TX 78704",
      "address": "1200 Barton Springs Rd",
      "city": "Austin",
      "state": "TX",
      "zip": "78704",
      "latitude": 30.2598,
      "longitude": -97.7544,
      "images": {
        "street_view": "https://img.dealmachine.com/sv/30.2598,-97.7544.jpg",
        "satellite": "https://img.dealmachine.com/sat/30.2598,-97.7544.jpg",
        "roadmap": "https://img.dealmachine.com/map/30.2598,-97.7544.jpg"
      },
      "estimated_value": 875000,
      "equity_percent": 72
    }
  ]
}
```

When no property filters are present, `property_match` is not needed and no `properties` array is included.

### `properties`

The nested `properties` array on each person when `property_match` is set. Each property includes the [always-included property fields](/concepts/response-format#properties) plus any property fields you requested.

| Field            | Type   | Description                                               |
| ---------------- | ------ | --------------------------------------------------------- |
| `dm_property_id` | string | DealMachine internal property ID                          |
| `full_address`   | string | Complete formatted address                                |
| `address`        | string | Street address line                                       |
| `city`           | string | City                                                      |
| `state`          | string | State abbreviation                                        |
| `zip`            | string | ZIP code                                                  |
| `latitude`       | number | Property latitude                                         |
| `longitude`      | number | Property longitude                                        |
| `images`         | object | Signed URLs for `street_view`, `satellite`, and `roadmap` |

### `totals`

The full count of matching entities across the entire result set, regardless of pagination.

```json theme={null}
{
  "totals": {
    "people": 412,
    "properties": 387
  }
}
```

| Field        | Type    | Description                                                                             |
| ------------ | ------- | --------------------------------------------------------------------------------------- |
| `people`     | integer | Total people matching the filters                                                       |
| `properties` | integer | Total properties connected via `property_match`. `0` when no property filters are used. |

### `credits`

A breakdown of what this request cost. Included on every search response.

```json theme={null}
{
  "credits": {
    "used": 25,
    "properties": 0,
    "people": 25,
    "deduplicated": 0
  }
}
```

| Field          | Type    | Description                                          |
| -------------- | ------- | ---------------------------------------------------- |
| `used`         | integer | New credits charged after deduplication              |
| `properties`   | integer | Property lead records evaluated in this response     |
| `people`       | integer | People lead records evaluated in this response       |
| `deduplicated` | integer | Entities already accessed this billing period (free) |

<Note>
  Credits are deduplicated within your billing period. If you've already accessed a person or
  property this month, it won't be charged again — it shows up in `deduplicated` instead.
</Note>

### `pagination`

| Field               | Type    | Description                                            |
| ------------------- | ------- | ------------------------------------------------------ |
| `page`              | integer | Current page number                                    |
| `per_page`          | integer | Results per page                                       |
| `total_results`     | integer | Total matching records (always equals `totals.people`) |
| `total_pages`       | integer | Total pages available                                  |
| `has_next_page`     | boolean | Whether more pages exist after this one                |
| `has_previous_page` | boolean | Whether pages exist before this one                    |

***

## How Property Filters Work

When you include property filters in a people search:

1. The API finds properties matching the property filters.
2. It looks up people connected to those properties via the `property_match` relationship.
3. It applies any people filters to narrow down the results.
4. Matching properties are nested in a `properties` array on each person.

**Property fields** you request appear on each object in the `properties` array. Some base property fields (`dm_property_id`, `address`, `city`, `state`, `zip`) are always included.

If no property filters are present, `property_match` is not needed and no `properties` array is included in the response.

***

## Notes

* At least one location, filter, list condition, or previous-export condition is required. An empty
  `filters` array is valid when another search criterion is present.
* All filters are combined with AND logic. See [Searching](/concepts/searching#filter-logic).
* `property_match` is **required** when any property filter is present, and **ignored** when only people filters are used.
* When `property_match` is set, matching properties appear in a nested `properties` array on each person. Any property fields you request show up there.
* All search results are enriched and **consume credits per entity**. Use `estimate_cost: true` to preview credit costs before searching.
* Use `source_type=people` and `source_type=properties` when calling [List Filters](/api-reference/filters/list-filters) and [List Fields](/api-reference/fields/list-fields) to discover available filters and fields.

***

## Cost Estimate

Set `estimate_cost: true` to preview the credit cost of a search **before committing to it**. The request is validated identically to a real search — same filters, same pagination — but no data is returned and no credits are consumed.

```bash theme={null}
curl -X POST "https://api.v2.dealmachine.com/v1/people/search" \
  -H "Authorization: Bearer dm_sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "estimate_cost": true,
    "locations": [
      { "type": "state", "code": "TX" }
    ],
    "property_match": "owner",
    "filters": [
      { "filter_id": "estimated_value", "operator": "greater_than", "value": 500000 }
    ],
    "page": 1,
    "per_page": 25
  }'
```

The response contains `totals`, `pagination`, and `estimated_credits` — but no `data` array:

```json theme={null}
{
  "totals": {
    "people": 412,
    "properties": 387
  },
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total_results": 412,
    "total_pages": 17,
    "has_next_page": true,
    "has_previous_page": false
  },
  "estimated_credits": {
    "this_page": 25,
    "total_all_pages": 412,
    "breakdown": {
      "people": 25,
      "properties": 0,
      "already_accessed": 0,
      "note": "Estimate based on total results. Actual credits may be lower due to deduplication within your billing period."
    }
  }
}
```

| Field                                          | Type    | Description                                             |
| ---------------------------------------------- | ------- | ------------------------------------------------------- |
| `estimated_credits.this_page`                  | integer | Estimated credits for this page before deduplication    |
| `estimated_credits.total_all_pages`            | integer | Credits all pages combined would consume                |
| `estimated_credits.breakdown.people`           | integer | People data credits on this page                        |
| `estimated_credits.breakdown.properties`       | integer | Property credits; currently `0` for people search       |
| `estimated_credits.breakdown.already_accessed` | integer | Currently `0`; final usage applies billing-period dedup |

<Tip>
  Cost estimate is free and does not consume credits. Use it as often as needed. To get just the
  total count without cost estimates, use [Count People](/api-reference/people/count-people).
</Tip>
