COQL vs Search Records in Zoho CRM — Limits, Speed, and When to Use Each

Sat Jan 18 2025
Zoho CRM · Query Design

COQL vs Search Records in Zoho CRM — Limits, Speed, and When to Use Each

Developers often ask whether to use COQL or the CRM Search Records API. This guide compares limits, performance, pagination, and real-world patterns so you can pick the right tool.

Mostafa Badawy··7 min read

Choose COQL when…

  • You need precise filters (AND/OR, ranges) and select only specific fields.
  • You want fewer, denser network calls (select only what you need).
  • You can work within row and field caps per query.

Choose Search Records when…

  • You’re doing keyword/criteria searches supported by the endpoint.
  • You need straightforward pagination (page/per_page) over large sets.
  • You want to avoid COQL’s field cap and fetch many fields/schema defaults.

Key differences at a glance

  • API shape: COQL executes a SQL-like select_query. Search Records takes query params/criteria and returns records with default field sets unless you specify fields.
  • Limits: COQL typically returns up to 200 rows and supportslimit <offset>, <count>. Search Records uses page/per_page(often up to 200 per page).
  • Performance: For complex filters where you only need a handful of columns, COQL can be effectively faster because you transfer less JSON. For wide objects or fuzzy searches, Search Records is often simpler and comparable.
  • Deluge vs REST: Use Deluge CRM tasks if they cover your use case; otherwise call REST viainvokeurl for COQL or advanced search.

COQL examples (REST via invokeurl)

Fetch only the fields you care about from Leads, up to 200 rows:

// COQL (REST) via invokeurl in Deluge — single page
body = {
  "select_query": "select Last_Name, Email, City from Leads where City = 'Dubai' limit 200"
};

resp = invokeurl
[
  url        : "https://www.zohoapis.com/crm/v8/coql"
  type       : POST
  parameters : body
  connection : "zoho_crm_all"
];

// Null-safe reads
rows = ifNull(resp.get("data"), list());
for each r in rows
{
  email = ifNull(r.get("Email"), "");
  // ...
}

COQL pagination:

// Fetch up to 10,000 rows (50 × 200). Adjust repeats for more/less pages.
// Uses "limit <offset>, 200" and stops when info.more_records == false.

repeats   = repeat(",", 49);     // 49 commas => 50 iterations in toList()
offset    = 0;
iteration = 0;
all_rows  = list();

for each r in repeats.toList()
{
  q = "select id, Last_Name, Email from Leads where City = 'Dubai' limit " + offset + ", 200";
  body = {"select_query": q};

  resp = invokeurl
  [
    url        : "https://www.zohoapis.com/crm/v8/coql"
    type       : POST
    parameters : body.toString()
    connection : "zoho_crm_all"
  ];

  data = ifNull(resp.get("data"), list());
  all_rows.addAll(data);

  // Early stop when server indicates no more pages
  if (response.getJSON("info").getJSON("more_records") == false)
  {
    break;
  }

  iteration = iteration + 1;
  offset    = iteration * 200;   // next page offset
}

info "Total fetched via COQL: " + all_rows.size();
Tips: COQL strings require single quotes around values. Keep field lists reasonable to avoid errors and reduce payload size.

Search Records examples (REST via invokeurl)

Keyword/criteria search with simple pagination:

// Make sure of scope=ZohoSearch.securesearch.READ

// Search Records (page/per_page)
per_page = 200;
page     = 1;

params = {
  "criteria": "(City:equals:Dubai)",
  "page"    : page,
  "per_page": per_page
};

resp = invokeurl
[
  url        : "https://www.zohoapis.com/crm/v8/Leads/search"
  type       : GET
  parameters : params
  connection : "zoho_crm_all"
];

rows = ifNull(resp.get("data"), list());

Search pagination (bounded loop + early break):

// Up to 10,000 rows (50 × 200). Increase repeats for more.
repeats   = repeat(",", 49);
per_page  = 200;
page      = 1;
all_rows  = list();

for each _ in repeats.toList()
{
  urlStr = "https://www.zohoapis.com/crm/v8/Leads/search?page=" + page.toString() + "&per_page=" + per_page.toString() + "&criteria=(City:equals:Dubai)";

  resp = invokeurl
  [
    url        : urlStr
    type       : GET
    connection : "zoho_crm_all"
  ];

  data = ifNull(resp.get("data"), list());
  all_rows.addAll(data);

  infoMap = ifNull(resp.get("info"), Map());
  more    = infoMap.get("more_records");

  // Stop when server says no more, or last page shorter than per_page
  if ((more != null && more == false) || data.size() < per_page)
  {
    break;
  }

  page = page + 1;
}

info "Total fetched via Search: " + all_rows.size();

Choosing by use case

  • Dashboards/light reads: need a few columns with precise filters → COQL.
  • Bulk/wide records: want many fields, okay with multiple pages → Search.
  • Hybrid: COQL to get IDs + key columns, then fetch full details in chunks.

Common limits & guardrails

  • Rows per call: COQL ≈ 200; Search per_page200.
  • COQL fields: keep field list reasonable (commonly ~50).
  • Rate limits (429): add retries/backoff in your calling layer if you hit quotas.
  • Null-safety: wrap reads with ifNull in Deluge.

Hybrid pattern: COQL + detail fetch

// 1) COQL to narrow set (IDs + a few columns)
body = {"select_query":"select id from Deals where Stage = 'Closed Won' limit 200"};
coql = invokeurl
[
  url        : "https://www.zohoapis.com/crm/v8/coql"
  type       : POST
  parameters : body.toString()
  connection : "zoho_crm_all"
];

ids = List();
for each id in coql.get("data").toList() {
      ids.add(id.get("id));  
}

// 2) Fetch details one-by-one
details   = list();

for each rid in ids_list
{
  resp = invokeurl
  [
    url        : "https://www.zohoapis.com/crm/v8/Deals/" + rid
    type       : GET
    connection : "zoho_crm_all"
  ];
  if(resp.get("data") != null) { details.add(resp.get("data") }
}

SEO mini-FAQ

  • “Is COQL faster than Search?” Often, if you select fewer fields and return less JSON.
  • “Max records per COQL call?” Typically 200 rows. Paginate with offset/count.
  • “When to avoid COQL?” When you need many fields from wide layouts or fuzzy search.