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

# Search contacts

# Search Contacts

Find contacts by email address, custom field values, or other criteria using the search API.

## Goal

By the end of this guide you will be able to search for contacts by email (useful for dedup checks), filter by custom field values, and paginate through large result sets.

## Prerequisites

* An API key with **`contacts:read`** scope
* Your API base URL (found on the Settings > API Keys page)
* A **contact structure ID** — search operates within a single contact structure. Retrieve yours with `GET /api/contact-structure` (see [Manage Custom Fields](./manage-custom-fields.md))

**Scope note:** Contact search uses `POST` but is semantically a read operation. It requires only `contacts:read` scope (not `contacts:write`).

## Steps

### 1. Search by email address

This is the most common search pattern, useful for deduplication before creating a contact.

```bash theme={null}
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 10,
    "source": ["_id", "key", "fields", "createdAt", "updatedAt"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "KEY",
              "operator": "EQ",
              "values": ["jane.smith@example.com"]
            }
          ]
        }
      ]
    }
  }'
```

**Response** (`200 OK`):

```json theme={null}
{
  "contacts": [
    {
      "_id": "66f1a6e4698f1bca60424901",
      "key": "jane.smith@example.com",
      "fields": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "Jane" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Smith" }
      ],
      "createdAt": "2026-03-28T14:30:00.000Z",
      "updatedAt": "2026-03-28T14:30:00.000Z",
      "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1"
    }
  ],
  "totalRecords": 1
}
```

**Dedup pattern:** Before creating a contact, search by email using `"operator": "EQ"`. If `totalRecords` is 0, the email is not in use and you can safely create the contact.

### 2. Search by email domain

Find all contacts from a specific email domain.

```bash theme={null}
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "EMAIL_DOMAIN",
              "operator": "EQ",
              "values": ["example.com"]
            }
          ]
        }
      ]
    }
  }'
```

### 3. Search by custom field values

Filter contacts based on custom field values. Use the field's `_id` from your contact structure.

```bash theme={null}
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields", "tags"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "FIELD_ID",
              "id": "64a1b2c3d4e5f6a7b8c9d102",
              "operator": "EQ",
              "values": ["Enterprise"]
            }
          ]
        }
      ]
    }
  }'
```

**Response** (`200 OK`):

```json theme={null}
{
  "contacts": [
    {
      "_id": "66f1a6e4698f1bca60424905",
      "key": "alice@enterprise-corp.com",
      "fields": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d100", "value": "Alice" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d101", "value": "Chen" },
        { "_id": "64a1b2c3d4e5f6a7b8c9d102", "value": "Enterprise" }
      ],
      "tags": [
        { "_id": "64a1b2c3d4e5f6a7b8c9d300" }
      ],
      "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1"
    }
  ],
  "totalRecords": 1
}
```

### 4. Combine multiple criteria

Use multiple criteria objects to build complex filters. Criteria within the same group are AND-ed; separate groups are OR-ed.

```bash theme={null}
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields", "createdAt"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "FIELD_ID",
              "id": "64a1b2c3d4e5f6a7b8c9d102",
              "operator": "EQ",
              "values": ["Enterprise"]
            },
            {
              "columnToFilter": "CREATED_AT",
              "operator": "GT",
              "values": ["2026-01-01T00:00:00.000Z"]
            }
          ]
        }
      ]
    }
  }'
```

This searches for contacts where the custom field equals "Enterprise" **AND** the contact was created after January 1, 2026.

### 5. Search within a specific list

Filter contacts that belong to a specific list.

```bash theme={null}
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 25,
    "source": ["_id", "key", "fields"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "LIST_ID",
              "operator": "IN",
              "values": ["66f1a6e4698f1bca60425001"]
            }
          ]
        }
      ]
    }
  }'
```

### 6. Paginate through results

For large result sets, increment the `page` parameter to retrieve subsequent pages. Pages are 1-indexed.

```bash theme={null}
# Page 1 (first 50 results)
curl -X POST https://api-us-west-2-c1.benchmarkemail.com/api/contact/search \
  -H "X-API-Key: bme_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "contactStructureId": "64a1b2c3d4e5f6a7b8c9d0e1",
    "page": 1,
    "pageSize": 50,
    "source": ["_id", "key"],
    "contactSpecification": {
      "filters": [
        {
          "criterias": [
            {
              "columnToFilter": "CONTACT_STATUS",
              "operator": "EQ",
              "values": ["active"]
            }
          ]
        }
      ]
    },
    "sortField": [
      {
        "column": "CREATED_AT",
        "order": "desc"
      }
    ]
  }'
```

**Response** (`200 OK`):

```json theme={null}
{
  "contacts": [
    { "_id": "66f1a6e4698f1bca60424950", "key": "newest@example.com" },
    { "_id": "66f1a6e4698f1bca60424949", "key": "second@example.com" }
  ],
  "totalRecords": 3847
}
```

Use `totalRecords` to calculate the total number of pages: `Math.ceil(totalRecords / pageSize)`. Then loop through pages 1 to N.

### Request body reference

| Field                  | Required | Description                                                                                                                          |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `contactStructureId`   | Yes      | The contact structure to search within                                                                                               |
| `page`                 | Yes      | Page number (1-indexed)                                                                                                              |
| `pageSize`             | Yes      | Results per page                                                                                                                     |
| `source`               | Yes      | Fields to include in results: `_id`, `key`, `fields`, `tags`, `lists`, `contactStatus`, `contactSubStatus`, `createdAt`, `updatedAt` |
| `contactSpecification` | Yes      | Object with a `filters` array of filter groups (see operators below)                                                                 |
| `sortField`            | No       | Array of sort criteria                                                                                                               |
| `showFieldIds`         | No       | Limit which field IDs appear in `fields` array                                                                                       |

### Available filter operators

| Operator      | Description            | Example use                     |
| ------------- | ---------------------- | ------------------------------- |
| `EQ`          | Equals                 | Exact email match               |
| `NEQ`         | Not equals             | Exclude a specific value        |
| `SW`          | Starts with            | Email prefix search             |
| `CONTAINS`    | Contains substring     | Partial name match              |
| `GT`          | Greater than           | Created after a date            |
| `LT`          | Less than              | Created before a date           |
| `BETWEEN`     | Between two values     | Date range                      |
| `NOT_BETWEEN` | Not between two values | Exclude a date range            |
| `IS_EMPTY`    | Field is empty         | Find contacts with missing data |
| `NOT_EMPTY`   | Field is not empty     | Find contacts with data present |
| `IN`          | In a set of values     | Match any of several list IDs   |
| `NIN`         | Not in a set           | Exclude specific values         |

### Available filter columns

| Column               | Description                                                         |
| -------------------- | ------------------------------------------------------------------- |
| `KEY`                | Email address                                                       |
| `EMAIL_DOMAIN`       | Email domain (e.g., `example.com`)                                  |
| `FIELD_ID`           | Custom field value (requires `id` parameter with the field's `_id`) |
| `TAG_ID`             | Tag assignment                                                      |
| `LIST_ID`            | List membership                                                     |
| `CONTACT_STATUS`     | Primary status (`active`, `inactive`, etc.)                         |
| `CONTACT_SUB_STATUS` | Secondary status                                                    |
| `CREATED_AT`         | Creation timestamp                                                  |
| `UPDATED_AT`         | Last update timestamp                                               |

## Common Errors

| Status | Error                  | Cause                                              | Fix                                                                                                   |
| ------ | ---------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `401`  | `UnauthorizedError`    | Invalid or inactive API key                        | Verify your key in Settings > API Keys                                                                |
| `403`  | `ForbiddenError`       | Key lacks `contacts:read` scope                    | Search requires `contacts:read` scope                                                                 |
| `400`  | `ValidationError`      | Missing required fields or invalid filter operator | Ensure `contactStructureId`, `page`, `pageSize`, `source`, and `contactSpecification` are all present |
| `429`  | `TooManyRequestsError` | Rate limit exceeded                                | Wait for the `Retry-After` period. See [Rate Limits](../rate-limits.md)                               |

## Next Steps

* [Manage Contacts](./manage-contacts.md) — create, update, or delete the contacts you find
* [Manage Lists](./manage-lists.md) — organize contacts into lists
* [Manage Custom Fields](./manage-custom-fields.md) — understand your contact structure's field IDs
* [Migration from Legacy](./migration-from-legacy.md) — use search for dedup during bulk import
