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

# Detect Duplicates

> Find likely duplicate contacts, companies, or deals using semantic matching

<Warning>
  This endpoint consumes AI credits. Each scan costs 3 credits for workspaces up to 10,000 records, 5 credits up to 100,000, and 10 credits beyond that. If the workspace balance is insufficient, the API returns `402 insufficient_credits`.
</Warning>

## Request

### Headers

```
Authorization: Bearer wbk_your_api_key_here
Content-Type: application/json
```

<ParamField header="Idempotency-Key" type="string">
  Optional UUID for retry deduplication. A 60-minute cache applies to the result.
</ParamField>

### Body Parameters

<ParamField body="entity_type" type="string" required>
  One of `contact`, `company`, or `deal`.
</ParamField>

<ParamField body="threshold" type="number" default="0.85">
  Minimum similarity score (0.0 - 1.0) for a pair to be flagged. Lower values surface more candidates; higher values reduce noise.
</ParamField>

<ParamField body="limit" type="integer" default="100">
  Maximum number of duplicate pairs to return in a single response. Maximum 1000.
</ParamField>

<ParamField body="scope" type="object">
  Optional additional filters: `created_after` (ISO 8601), `tag`, `list_id`.
</ParamField>

## Response

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="pairs" type="array">
      <Expandable title="Duplicate pair">
        <ResponseField name="primary_id" type="string">ID of the suggested primary record</ResponseField>
        <ResponseField name="duplicate_id" type="string">ID of the suggested duplicate</ResponseField>
        <ResponseField name="similarity" type="number">Confidence score (0.0 - 1.0)</ResponseField>
        <ResponseField name="matched_fields" type="array">Fields that contributed to the match (e.g. `email`, `full_name`, `phone`)</ResponseField>
        <ResponseField name="reason" type="string">Short natural-language explanation</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="total_candidates" type="integer">Number of records scanned</ResponseField>
    <ResponseField name="credits_remaining" type="integer">Balance after this call</ResponseField>
  </Expandable>
</ResponseField>

Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `X-Request-ID`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    https://data.leadlex.com/functions/v1/api-gateway/v1/ai/detect-duplicates \
    -H "Authorization: Bearer wbk_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{"entity_type": "contact", "threshold": 0.9}'
  ```

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

  API_KEY = "wbk_your_api_key_here"
  BASE_URL = "https://data.leadlex.com/functions/v1/api-gateway"

  r = requests.post(
      f"{BASE_URL}/v1/ai/detect-duplicates",
      headers={
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      },
      json={"entity_type": "contact", "threshold": 0.9, "limit": 200},
  )
  for pair in r.json()["data"]["pairs"]:
      print(pair["primary_id"], "<=>", pair["duplicate_id"], pair["similarity"])
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    'https://data.leadlex.com/functions/v1/api-gateway/v1/ai/detect-duplicates',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer wbk_your_api_key_here',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ entity_type: 'contact', threshold: 0.9 }),
    }
  );
  const { data } = await res.json();
  console.log(data.pairs.length);
  ```
</CodeGroup>

### Example Response

```json theme={null}
{
  "data": {
    "pairs": [
      {
        "primary_id": "123e4567-e89b-12d3-a456-426614174000",
        "duplicate_id": "aaaa1111-bbbb-2222-cccc-333344445555",
        "similarity": 0.96,
        "matched_fields": ["email", "full_name", "company_name"],
        "reason": "Both records share the same email domain and nearly identical name."
      }
    ],
    "total_candidates": 4821,
    "credits_remaining": 4863
  }
}
```

## Errors

| Status | Code                       | Description                                      |
| ------ | -------------------------- | ------------------------------------------------ |
| 400    | `validation_error`         | Unsupported `entity_type` or invalid `threshold` |
| 401    | `invalid_key`              | Invalid or expired API key                       |
| 402    | `insufficient_credits`     | Workspace credit balance is exhausted            |
| 403    | `insufficient_permissions` | Missing `write:ai` permission                    |
| 429    | `rate_limited`             | Rate limit exceeded                              |
