Duplicate records in Zoho can be surfaced programmatically using a dedicated search endpoint, letting you identify and act on redundant data before it causes reporting or workflow issues.
Why this matters
Duplicate records inflate pipeline numbers, confuse support agents, and undermine automation rules that assume unique entries. Whether you are managing a product catalogue in Zoho Desk or contacts in Zoho CRM, catching duplicates early keeps your data clean and your processes reliable. As independent expert support (not official Zoho support), Beam Help walks you through the mechanics so your team can automate this check confidently.
Step-by-step
Step 1. Ensure your OAuth connection carries the correct scopes before making any API call. For Zoho CRM you need at minimum ZohoCRM.modules.ALL or equivalent read permissions, and for Zoho Desk you need Desk.search.READ alongside the relevant module scopes such as Desk.contacts.READ. [1]
Step 2. Authenticate and obtain a valid access token. The platform stores your connection details (access token, refresh token, API domain, and expiry timestamp) in a connection record keyed to your user account. Tokens are automatically refreshed roughly two minutes before they expire to prevent mid-request failures, so make sure your integration follows the same pattern. [5]
Step 3. If your token has expired, trigger a refresh by calling ZohoOAuth.refreshtokens() with the stored refresh token. On success, persist the new accesstoken and updated tokenexpiresat values back to your connection store so subsequent calls remain authenticated. [3]
Step 4. To retrieve duplicate records, issue a GET request to the following endpoint:
GET /api/v1/products/duplicate
This operation is identified internally as searchforduplicate_records and accepts an optional parameter dictionary (p) that you can use to filter or paginate results. [6]
Step 5. Pass any relevant filter parameters inside the p dictionary. The exact supported keys depend on your Zoho Desk configuration, but the parameter object is forwarded directly to the Zoho API, so consult your module's field list to narrow results (for example, by product category or date range). [6]
Step 6. Handle the response. A successful call returns a structured result object. If the result contains an error key, surface that message to the operator and halt further processing. If the result is clean, iterate over the returned records to review, merge, or flag them as needed. [8]
Step 7. When building links back to individual duplicate records in your UI, use the dc (data centre), crmorgid, deskorgid, and desk_portal values stored in your connection record to construct correct region-specific URLs. [2]
Common pitfalls
- Missing scopes. If
Desk.search.READis absent from your OAuth grant, the duplicate search endpoint will return an authorisation error. Double-check the full scope list in your OAuth client configuration. [1] - Stale tokens. Calling the endpoint with an expired access token produces a 401 response. Build in proactive token refresh (at least 120 seconds before expiry) to avoid this mid-request. [5]
- Wrong data centre. Zoho hosts data in multiple regions. If your
apidomainpoints tozohoapis.eubut your client is configured forzohoapis.com, requests will fail silently or return empty results. Extract the data centre suffix from theapidomainstored during OAuth callback and use it consistently. [7] - Empty
pparameter. PassingNoneinstead of an empty dict{}for thepargument may cause unexpected behaviour depending on how the underlying HTTP client serialises the request. Default to{}when no filters are needed. [6]
What to check
- Scopes are present: Confirm
Desk.search.READ(and any module-level read scopes) appear in your active OAuth grant before calling the duplicate endpoint. [1] - Token is fresh: Verify that
tokenexpiresatis at least 120 seconds in the future at the time of the request, and that your refresh logic updates the stored value on success. [5] - Endpoint returns data: Confirm the response from
GET /api/v1/products/duplicatecontains a non-empty result set and noerrorkey before proceeding with any merge or deletion workflow. [6]