For AI agents: markdown of this page — /docs-content-en/duplicates.md documentation index — /llms.txt

Duplicate search

POST /v1/duplicates/find

Finds CRM leads, contacts, and companies whose phone number or email address matches the supplied values. Use it before creating a new record to avoid duplicates, when importing data from external sources, and when you need to find a record by phone number.

Bitrix24 API: crm.duplicate.findbycomm Scope: crm

Request fields (body)

Field Type Required Description
type string yes Value type to search by: phone or email
values array yes List of values to check — phone numbers or email addresses. Maximum 20 values per request
entityType string no Restrict the search to a single type: lead, contact, or company. If omitted, the search covers all three types

Examples

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/duplicates/find \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "phone",
    "values": ["+12025550123"]
  }'

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/duplicates/find \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "phone",
    "values": ["+12025550123"]
  }'

JavaScript — personal key

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/duplicates/find', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'phone',
    values: ['+12025550123'],
  }),
})

const { success, data } = await res.json()

JavaScript — OAuth application

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/duplicates/find', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'phone',
    values: ['+12025550123'],
  }),
})

const { success, data } = await res.json()

Response fields

Field Type Description
success boolean Always true on success
data object | array Matches found, grouped by entity type. Empty array [] when there are no matches
data.lead array IDs of leads with matching contact details. Full records: GET /v1/leads
data.contact array IDs of contacts with matching contact details. Full records: GET /v1/contacts
data.company array IDs of companies with matching contact details. Full records: GET /v1/companies

Response example

Matches found — data contains one key per entity type:

JSON
{
  "success": true,
  "data": {
    "lead": [105],
    "contact": [42]
  }
}

No matches — data comes back as an empty array:

JSON
{
  "success": true,
  "data": []
}

Error response example

400 — required parameters not supplied:

JSON
{
  "success": false,
  "error": {
    "code": "MISSING_PARAMS",
    "message": "Required: type (\"PHONE\" or \"EMAIL\"), values (string array, max 20)"
  }
}

Errors

HTTP Code Description
400 MISSING_PARAMS type or values not supplied, or values is an empty array
400 TOO_MANY_VALUES More than 20 values in values
422 BITRIX_ERROR Invalid type — the error text is returned in the message field
403 SCOPE_DENIED The key lacks the crm scope
401 TOKEN_MISSING The key has no access tokens configured

Full list of common API errors — Errors.

Known specifics

A number is matched in any format and against any of the numbers stored on the record. The values +1 (202) 555-0123, +12025550123, and 12025550123 all find the same record, no matter how the number is stored in the Bitrix24 account. If a contact has both a work and a mobile phone, the method finds it by either of them. That is why you look up a record by number with this method rather than with the filter.phone filter in list or search requests: that filter compares the string against the stored value in full and sees only the record's first number — Filter by phone and email. Pass the number in full: without the country code it is not recognized.

The data type depends on the result. When there are matches, it is an object keyed by entity type. When there are none, it is an empty array []. Check the type before processing — Array.isArray(data) means no matches were found.

A single value can match several types at once. If a phone number or email address exists on both a lead and a contact, the response includes both keys with their own IDs. Without entityType, check every returned group, not just the first.

See also