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

# Get Contact by Email

> Retrieve a single contact by its email address

## Overview

This endpoint looks up exactly one contact by email address, when you already expect a single match. **The success response is byte-identical to [Get Contact](/api-reference/endpoint/get-contact)** — same fields (`id`, `emails`, `phones`, `names`, `locations`, `custom_fields`) and the same optional extras, all of which behave identically here. See [Get Contact](/api-reference/endpoint/get-contact) for the full field-by-field response breakdown; it isn't repeated on this page.

You can address the contact either of two ways:

* **By path**: `GET /contacts/by-email/{email}`
* **By query string**: `GET /contacts/by-email?email={email}`

Both forms resolve identically and use the same rate limit. The path form accepts either a raw `@` or a percent-encoded `%40` in the `{email}` segment.

Resolution matches the `email` filter on [List Contacts](/api-reference/endpoint/list-contacts): matching is exact (not partial or fuzzy) and case-insensitive, with surrounding whitespace trimmed. It matches **both** the contact's primary address and any of its alternate addresses, including addresses captured on profiles that were later merged into the contact.

Because this endpoint addresses a single contact, more than one match is an error rather than an arbitrary pick — see the `409` response below.

<Tip>
  Use this endpoint when you expect exactly one contact for the address. Use [List Contacts](/api-reference/endpoint/list-contacts) with its `email` filter instead when you want to see every contact that shares an address, need to batch up to 100 addresses in one call, or want to avoid the possibility of a `409` entirely.
</Tip>

## Path Parameters

<ParamField path="email" type="string">
  The email address of the contact to retrieve, when using the path form `GET /contacts/by-email/{email}`. Required when not using the query form. Accepts a raw `@` or a percent-encoded `%40`.
</ParamField>

## Query Parameters

<ParamField query="email" type="string">
  The email address of the contact to retrieve, when using the query form `GET /contacts/by-email?email=`. Required when not using the path form.
</ParamField>

<ParamField query="include_comet_tokens" type="boolean" default="0">
  When set to `1`, includes the last 5 comet tokens associated with this contact, ordered by most recent first. Accepts `1` or `0`.
</ParamField>

<ParamField query="include_events" type="boolean" default="0">
  When set to `1`, includes the contact's full event journey — all conversions and touchpoints sorted by most recent first. Accepts `1` or `0`.
</ParamField>

<ParamField query="hide_direct_touchpoints" type="boolean" default="1">
  When set to `1` (default), direct touchpoints are excluded from the events list. Set to `0` to include them. Only applies when `include_events=1`. Accepts `1` or `0`.
</ParamField>

<ParamField query="use_custom_field_labels" type="boolean" default="0">
  When set to `1`, custom field keys in the `custom_fields` object will use the user-defined labels (e.g. `"Customer Age"`) instead of the raw column names (e.g. `"profile_field_1"`). Fields without a configured label will keep their raw column name. Accepts `1` or `0`.
</ParamField>

<ParamField query="include_browsing_session_data" type="boolean" default="0">
  When set to `1`, includes up to the last 1000 raw browsing session hits (page views) for this contact, ordered by most recent first. Accepts `1` or `0`.
</ParamField>

## Response

### Success Response

Returns the contact object directly (not wrapped in a `data` property) — the same shape returned by [Get Contact](/api-reference/endpoint/get-contact), including all of its optional fields (`comet_tokens`, `events`, `browsing_session_hits`) under the same query parameters.

### Error Response

<ResponseField name="message" type="string">
  Error description explaining what went wrong.
</ResponseField>

<ResponseField name="contact_ids" type="integer[]">
  Only present on a `409` response. The sorted list of canonical contact ids that matched the given email.
</ResponseField>

## Example Requests

### Look Up by Path

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://app.cometly.com/public-api/v1/contacts/by-email/jane%40acme.com" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Accept: application/json" \
    -H "Content-Type: application/json"
  ```

  ```javascript JavaScript theme={null}
  const email = 'jane@acme.com';
  const response = await fetch(`https://app.cometly.com/public-api/v1/contacts/by-email/${encodeURIComponent(email)}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  });

  const data = await response.json();
  ```

  ```php PHP theme={null}
  <?php
  $email = 'jane@acme.com';
  $url = 'https://app.cometly.com/public-api/v1/contacts/by-email/' . rawurlencode($email);
  $headers = [
      'Authorization: Bearer YOUR_API_KEY',
      'Accept: application/json',
      'Content-Type: application/json'
  ];

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

  $response = curl_exec($ch);
  $data = json_decode($response, true);
  curl_close($ch);
  ?>
  ```

  ```python Python theme={null}
  import requests
  from urllib.parse import quote

  email = 'jane@acme.com'
  headers = {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
  }

  url = f'https://app.cometly.com/public-api/v1/contacts/by-email/{quote(email, safe="")}'
  response = requests.get(url, headers=headers)
  data = response.json()
  ```
</CodeGroup>

### Look Up by Query String

<CodeGroup>
  ```bash cURL theme={null}
  curl -G "https://app.cometly.com/public-api/v1/contacts/by-email" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Accept: application/json" \
    -H "Content-Type: application/json" \
    -d "email=jane@acme.com"
  ```

  ```javascript JavaScript theme={null}
  const email = 'jane@acme.com';
  const params = new URLSearchParams({ email });

  const response = await fetch(`https://app.cometly.com/public-api/v1/contacts/by-email?${params}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  });

  const data = await response.json();
  ```

  ```php PHP theme={null}
  <?php
  $email = 'jane@acme.com';
  $url = 'https://app.cometly.com/public-api/v1/contacts/by-email?' . http_build_query(['email' => $email]);
  $headers = [
      'Authorization: Bearer YOUR_API_KEY',
      'Accept: application/json',
      'Content-Type: application/json'
  ];

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

  $response = curl_exec($ch);
  $data = json_decode($response, true);
  curl_close($ch);
  ?>
  ```

  ```python Python theme={null}
  import requests

  email = 'jane@acme.com'
  url = 'https://app.cometly.com/public-api/v1/contacts/by-email'
  headers = {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Accept': 'application/json',
      'Content-Type': 'application/json'
  }

  response = requests.get(url, headers=headers, params={'email': email})
  data = response.json()
  ```
</CodeGroup>

## Status Codes

| Status Code | Description                                                                               |
| ----------- | ----------------------------------------------------------------------------------------- |
| 200         | Exactly one contact matched and was retrieved                                             |
| 401         | Missing or invalid API key                                                                |
| 403         | API key doesn't have permission or subscription is inactive                               |
| 404         | No contact matches the given email                                                        |
| 409         | More than one contact matches the given email — see `contact_ids` in the response         |
| 422         | Invalid parameters (missing or malformed email)                                           |
| 429         | Too many requests - rate limit exceeded. See [Rate Limiting](/introduction/rate-limiting) |

## Notes

* **Rate Limit**: This endpoint has a limit of **30 requests per minute** per Space — the same bucket as [Get Contact](/api-reference/endpoint/get-contact), not the 15/minute limit on [List Contacts](/api-reference/endpoint/list-contacts). See [Rate Limiting](/introduction/rate-limiting) for details.
* **Resolution**: Matching is exact and case-insensitive, with surrounding whitespace trimmed. It matches **both** the contact's primary address and any of its alternate addresses, including addresses captured on profiles that were later merged into the contact — the identical algorithm used by the `email` filter on [List Contacts](/api-reference/endpoint/list-contacts).
* **Multiple matches are a `409`, not a pick**: several distinct contacts can legitimately share an address. When that happens, nothing is guessed — the response is `{"message": "Multiple contacts match this email. Fetch them with GET /contacts?email= and then request one by its contact id.", "contact_ids": [...]}`. Either call [List Contacts](/api-reference/endpoint/list-contacts) with its `email` filter to see all of the matches, or request one of the returned `contact_ids` directly via [Get Contact](/api-reference/endpoint/get-contact).
* **No match is a `404`**: unlike the list-based `email` filter (which returns a normal `200` with an empty array), this endpoint returns `404` with `{"message": "Contact not found"}` when nothing matches, because it addresses a single resource.
* **Same payload as Get Contact**: the response body, including all optional fields and query parameters, is identical to [Get Contact](/api-reference/endpoint/get-contact). Nothing here can drift from that endpoint's documented shape.
