Kaizen #207 - Answering your Questions | Advanced Queries using COQL API

Kaizen #207 - Answering your Questions | Advanced Queries using COQL API



Hi everyone, and welcome to another Kaizen week!

As part of Kaizen #200 milestone, many of you shared topics you would like us to cover, and we have been addressing them one by one over the past few weeks. Today, we are picking up one of those requests - a deep dive into advanced queries using Zoho CRM’s COQL APIs.

When you start building complex applications on top of Zoho CRM, you may feel that basic record fetch APIs like GET records are not enough. Business logic often demands far more, like combining data from multiple modules, applying conditional filters, grouping results, or even running aggregate calculations.

This is exactly where COQL (CRM Object Query Language) shines. If you have already used COQL for straightforward queries, this post will help you go further. 

COQL Recap - Why Use It?

COQL is your go-to when you need more than what the standard GET Records API can provide. It gives you:

  • SQL-like flexibility for querying CRM data.
  • Access to related records across multiple modules using lookups and joins.
  • Powerful filtering, aggregation, and sorting well beyond simple searches.

In short: use COQL when you want fine-grained control over results, complex reporting logic, or performance improvements in large data environments.

Understanding COQL Queries

The Query API (COQL) is best when you need flexible record retrieval without chaining multiple API calls or creating custom views.

Instead of building and maintaining complex filters in the UI, you can describe your data needs in one SQL-like query.

For example, with COQL you can:

  • Fetch all products within a certain price range that also have a 5-star rating, sorted by price.
  • Filter deals based on details from a related module, like the Vendor’s status.
  • Pull a precise slice of data on-demand without altering CRM views.

What kind of queries are supported?

COQL currently supports only the SELECT statement, which lets you pick fields, apply conditions, sort results, and control pagination.

A typical query looks like this:

SELECT {field_api_namesFROM {module_api_nameWHERE {field_api_name} {comparator} {valueGROUP BY {field_api_name} ORDER BY {field_api_nameASC/DESC LIMIT {limitOFFSET {offset}

  • FROM - specifies the module to query
  • WHERE - filters records based on conditions

  • GROUP BY - groups records by one or more fields for aggregation
  • ORDER BY - sorts results ascending or descending
  • LIMIT - restricts the number of records returned
  • OFFSET - skips a certain number of records before fetching results

Example:

{

 "select_query" : "select Last_Name, First_Name, Mobile, Final_Score from Leads where Lead_Status 'Not Contacted' order by Final_Score desc limit 5 offset 10"

}


The above query retrieves five Leads who have not been contacted, ordered by Final_Score, skipping the first 10(OFFSET). You can also use the shorthand LIMIT offset, limit:

{

 "select_query" : "select Last_Name, First_Name, Mobile, Final_Score from Leads where Lead_Status = 'Not Contacted' order by Final_Score desc limit 10, 5"

}


Supported Data Types & Operators

COQL supports multiple field types, each with dedicated operators:

Field Type

Supported Operators

Text, Picklist, Email, Phone, Website, Autonumber 

=, !=, like, not like, in, not in, is null, is not null

Lookup

=, !=, in, not in, is null, is not null

Date, DateTime, Number, Currency

=, !=, >=, >, <=, <, between, not between, in, not in, is null, is not null

Boolean

=

Formula

If the return type is:

  1. Decimal/Currency/Date/Datetime: =, !=, >=, >, <=, <, between, not between, in, not in, is null, is not null
  2. String: =, !=, like, not like, in, not in, is null, is not null
  3.  Boolean: =


Queries can be further refined with sorting (ORDER BY) and pagination using LIMIT and OFFSET.

Aggregate functions

COQL supports aggregate functions to summarize data:

  • SUM(field) – total of numeric values
  • MAX(field) – largest value
  • MIN(field) – smallest value
  • AVG(field) – average value
  • COUNT(*) – number of records matching criteria 


Please note that aggregate functions are supported only for numeric data types such as number, decimal, currency, etc.

Wildcards

The % character is supported with the LIKE operator for flexible text matching:

  • '%tech' → values ending with “tech”
  • 'C%' → values starting with “C”
  • '%tech%' → values containing “tech”


With these building blocks, you can already express a wide range of queries. Let’s now move into advanced scenarios where COQL really shines.

Beyond basics: COQL Patterns for real-world scenarios

Once you are comfortable with the basics of COQL, you can start combining them into more powerful query patterns. Some of these go beyond simple filtering and field selection, helping you minimize API calls, handle relationships, and emulate unsupported features.

1. Advanced Filtering & Conditions

Beyond equality, COQL supports operators like LIKEINBETWEEN, and date comparisons.

Example: Fetch Leads from the IT or Finance industry created in the year 2025.

{

 "select_query": "select Full_Name, Industry from Leads where Industry in ('IT', 'Finance') and Created_Time between '2025-01-01T00:00:00+05:30' and '2025-12-31T23:59:59+05:30'"

}


Use case: Run targeted campaigns or segment leads for analysis without multiple API calls.

2. Combining Multiple Conditions

You can query diverse conditions, combining exact, partial matches, and set memberships.

Example: Pre-qualified leads in target industries with company names containing “zylker”:

{

 "select_query": "select First_Name, Last_Name from Leads where (((Lead_Status = 'Pre-Qualified') and (Company like '%zylker%')) and Industry in ('Technology', 'Government/Military'))"

}



Use case: Sophisticated audience segmentation or analytics for campaigns.

3. Fetching related records and their fields using Joins (Dot notation)

COQL allows you to retrieve related records efficiently by navigating lookup relationships using dot notation. This makes it possible to pull in contextual information across modules without chaining multiple API calls.

Single-level join: Fetch contacts and their account names, excluding a specific account:

{

 "select_query": "select Last_Name, First_Name, Account_Name.Account_Name, Owner from Contacts where (Account_Name.Account_Name != 'Zylker') limit 2"

}


Sample use case: Retrieve all contacts along with their associated account names while excluding certain accounts (e.g., competitors or internal test accounts). This avoids multiple queries across modules and helps in cleaner campaign targeting.

Hierarchical / nested join: Fetch contacts whose accounts have a parent account named “Kings”:

{

 "select_query": "select Account_Name, Account_Name.Parent_Account.Account_Name from Contacts where Account_Name.Parent_Account.Account_Name = 'Kings' limit 5"

}


Sample use case: Easily retrieve multi-level relationships such as parent-child accounts for reporting, territory alignment, or hierarchical sales analysis.

Multi-Level Join with Extended Lookup : Fetch contacts, their accounts, the parent accounts of those accounts, and the owner of the parent account:

{

 "select_query": "select Last_Name, First_Name, Account_Name.Account_Name, Account_Name.Parent_Account, Account_Name.Parent_Account.Owner AS 'Parent Account Owner', Owner from Contacts where (Account_Name.Account_Name != 'Zylker') limit 2"

}


Sample use case: Useful in complex account management and escalation scenarios where responsibility spans multiple levels. For instance, sales managers may want to see not just the contact and their account, but also which parent account owner is responsible for the overall relationship. These types of queries are helpful in large enterprises with layered ownership structures.

4. Using Subqueries to detect missing relationships

COQL supports subqueries to filter based on related module data or detect missing relationships.

Example: Find contacts whose accounts have no closed deals:

{

 "select_query": "select Full_Name, Email from Contacts where Account_Name not in (select Account_Name from Deals where Stage = 'Closed Won')"

}


Use case: Identify potential follow-ups, audit compliance, or uncover opportunities.

These types of queries are handy for:

  • Sales follow-ups – identify contacts from accounts that haven’t yet converted.
  • Compliance checks – ensure certain accounts meet deal requirements.
  • Pipeline building – target untouched accounts for new opportunities.

By combining subqueries with conditions like NOT IN, COQL makes it easy to surface hidden opportunities that would otherwise require multiple API calls and custom logic.

NoteSubqueries in COQL can return a maximum of 100 records. If the inner query has more than 100 matches, any extra records are ignored. This means you may get incomplete results in larger datasets. In such cases, it is better to redesign the query using joins or multiple API calls, which can handle broader datasets without this limit.

Advanced COQL Querying: Real-World Patterns

Once you’ve mastered filters, joins, and subqueries, you can combine them for advanced business logic. 

1. Filtering Deals Based on Account Attributes

Generic Use Case:
You want to prioritize deals connected to high-value accounts that meet specific business criteria, such as strong credit ratings or key industries.

Retrieve all deals for accounts that:

  • Have a high credit rating (>750)
  • Belong to a specific industry, e.g., Communications

COQL Query:

{

 "select_query": "SELECT Deal_Name, Amount, Account_Name, Contact_Name.Email FROM Deals WHERE Account_Name in (SELECT id FROM Accounts WHERE Credit_Rating > 750 AND Industry = 'Communications') AND Stage != 'Closed Won'"

}


Dynamically filter deals by account attributes while fetching related contact details in a single query.

2. Emulating MIN/MAX for Date fields

When working with date fields in COQL, a common analytical need is to compare records against the latest date from a related subset. For example, identifying deals that closed before the most recent high-value deal.

Intuitively, one might try to use aggregate functions like MAX() on a date field in a subquery, such as:

{

 "select_query": "SELECT Deal_Name FROM Deals WHERE Closing_Date < (SELECT MAX(Closing_Date) FROM Deals WHERE Amount > 500000)"

}

Warning
However, COQL currently does not support aggregate functions like MAX() or MIN() on Date or DateTime fields. Attempting this will result in errors or unexpected behavior, as COQL aggregates are primarily designed for numeric fields.

Workaround: Using Subquery with ORDER BY and LIMIT

Instead of MAX(), the recommended COQL approach leverages sorting and limiting the result set to a single latest date within a subquery:

{

 "select_query": "select Deal_Name from Deals where Closing_Date < (select Closing_Date from Deals where Amount > 500000 order by Closing_Date desc limit 1)"

}


How this works:

The inner subquery fetches the single most recent Closing_Date where deals exceed $500,000, ordering by date descending and limiting to one record. The outer query then retrieves all deals closed before that date.

This pattern mimics the MAX() date comparison in a manner supported by COQL’s current capabilities. You can apply the same approach with ascending sort order to emulate MIN() as well.

3. Combining Multiple Subqueries for Complex Business Logic

Real-world CRM scenarios often require filtering records based on multiple interconnected conditions across different modules. Consider this sales intelligence use case: you want to identify Contacts who are:

  • Associated with Accounts that have annual revenue greater than $1000000000000
  • Connected to Deals that were created in the previous quarter    

{

 "select_query": "SELECT First_Name, Last_Name, Email, Account_Name.Account_Name FROM Contacts WHERE Account_Name in (SELECT Account_Name FROM Deals WHERE Created_Time >= '2025-06-01' AND Account_Name in (SELECT id FROM Accounts WHERE Annual_Revenue > 1000000000000)) "

}


Query Breakdown:

  • Innermost subquery: (SELECT id FROM Accounts WHERE Annual_Revenue > 1000000000000) identifies high-revenue accounts
  • Middle subquery: (SELECT Account_Name FROM Deals WHERE Created_Time >= '2022-07-02T15:18:31+05:30' AND Account_Name in (...)) filters for accounts with deals created after the specified date that are also high-revenue accounts
  • Main query: Retrieves contact details for all contacts associated with these filtered accounts while fetching the related account names via JOIN


This pattern finds contacts from high-value accounts that have had recent deal activity, combining temporal filtering with revenue-based account qualification in a single efficient query.

Dynamic Account and Deal Performance Analysis

Imagine you need to find all Leads from industries where accounts have historically closed high-value deals (over $100K) and those leads have "Hot" ratings.

This requires filtering leads based on:

  • Industry performance from accounts with successful deals
  • Lead rating criteria
  • Retrieving lead details with industry information


{

 "select_query": "SELECT First_Name, Last_Name, Lead_Source, Company, Industry FROM Leads WHERE Industry in (SELECT Industry FROM Accounts WHERE id in (SELECT Account_Name FROM Deals WHERE Amount > 100000 AND Stage = 'Closed Won') GROUP BY Industry) AND Rating = 'Hot'"

}


What makes this powerful:

  • Inner subquery (SELECT Account_Name FROM Deals WHERE Amount > 100000 AND Stage = 'Closed Won') identifies accounts with successful high-value deals
  • Outer subquery (SELECT Industry FROM Accounts WHERE id in (...) GROUP BY Industry) gets the industries of those successful accounts
  • Groups by Industry to get unique industry values and avoid duplicates
  • Main query finds leads in those proven successful industries with "Hot" ratings.

    Note: Both subqueries in this query are limited to 100 records each. If either the Deals or Accounts module returns more than 100 matches, the additional records are silently ignored. This can lead to incomplete results when working with larger datasets. For scenarios where the inner queries are expected to return more than 100 records, redesign the query using joins or break it down into multiple API calls for complete coverage.


Conclusion

By going beyond simple record fetches, COQL gives you the power to do true analytics and querying. By mastering patterns that range from straightforward joins to complex multi-module subqueries, you can consolidate multiple API calls into a single query, reduce complexity, and streamline performance. At the same time, dynamic filtering across modules facilitates richer business logic, while relationship-aware queries let you build automations that can handle real-world exceptions with precision.

As you implement these patterns, remember that the most powerful COQL queries often combine multiple techniques: JOINs for data enrichment, subqueries for dynamic filtering, and careful aggregation for performance optimization. However, it is equally important to understand COQL's limitations too. Being aware of these limitations will help you design effective workarounds and choose the right approach for your specific use cases. For a comprehensive list of limitations, please refer to our COQL Limitations documentation.

Start with simpler patterns and gradually build complexity as your use cases demand. The investment in mastering COQL will pay you with cleaner code base, better performance, lesser credit consumption, and more sophisticated CRM functionality.

We hope that you found this post on COQL useful. If you have any queries or need further assistance, please feel free to comment below or email us at support@zohocrm.com. We are here to help!



    • Sticky Posts

    • Kaizen #198: Using Client Script for Custom Validation in Blueprint

      Nearing 200th Kaizen Post – 1 More to the Big Two-Oh-Oh! Do you have any questions, suggestions, or topics you would like us to cover in future posts? Your insights and suggestions help us shape future content and make this series better for everyone.
    • Kaizen #226: Using ZRC in Client Script

      Hello everyone! Welcome to another week of Kaizen. In today's post, lets see what is ZRC (Zoho Request Client) and how we can use ZRC methods in Client Script to get inputs from a Salesperson and update the Lead status with a single button click. In this
    • Kaizen #222 - Client Script Support for Notes Related List

      Hello everyone! Welcome to another week of Kaizen. The final Kaizen post of the year 2025 is here! With the new Client Script support for the Notes Related List, you can validate, enrich, and manage notes across modules. In this post, we’ll explore how
    • Kaizen #217 - Actions APIs : Tasks

      Welcome to another week of Kaizen! In last week's post we discussed Email Notifications APIs which act as the link between your Workflow automations and you. We have discussed how Zylker Cloud Services uses Email Notifications API in their custom dashboard.
    • Kaizen #216 - Actions APIs : Email Notifications

      Welcome to another week of Kaizen! For the last three weeks, we have been discussing Zylker's workflows. We successfully updated a dormant workflow, built a new one from the ground up and more. But our work is not finished—these automated processes are
    • Recent Topics

    • emailing estimates

      Shows up in the customer mail logs as sent but nobody is receiving them, even when I send them to myself I don't get them ??? Something wrong with the mail server or my end ?
    • Powering Customer Support with our women

      In Zoho Desk support, women make up 50% of our team. We see this as one of our strengths, reflecting the spirit of this year’s theme, "Give to Gain". Our women find their balance Women carry many responsibilities — they represent frontline support, lead
    • How to use OR when filtering using two fields

      I want to create return a list of Account Names by filtering on Field1 = "yes" OR Field 2 = "no" I can't see how to use the OR in the filter.
    • Mobile phone version not working well

      I am working on the Zoho Site Builder. In the preview the desktop version looks okay, but in the mobile phone preview many words are cut off in the weirdest (wrong) way. How can I fix that?
    • Zoho - Please explain difference between Thread view and Conversation view on Ticket

      I have reviewed the help document here but am still not clear on the difference between the two views. As an example, I just had a back and forth on a ticket: - Customer emails support email. - I email back from Desk. - Customer responds back. - I email back from Desk. On the upper left drop down box on the ticket Zoho Desk now says this is "4 Threads" and "4 Conversations" . How is that 4 threads?? By my count it is 1 thread and 4 conversations (assuming by "conversation" Zoho means number of total
    • Can't rename groups on Mac desktop app

      I'm working on an up-to-date Mac with a freshly downloaded Notebook app. I'm trying to rename a group within a notebook. Here I have, left to right, a note, a group, and a note. I select the group. On the top left, I select Action. On the dropdown, "Rename"
    • Zoho Forms - Feature Request - Year Field

      Hi Zoho Forms Team, You currently have the following date and time fields: Date Time Date and Time Year and Month It would be useful if you could include a "Year" field For example a recent application I completed said "What year was your house built?"
    • I need help to take Reports for tickets moved between departments.

      Hi, I need help to take Reports for tickets moved between different departments. Pls guide Shyam
    • Export to excel stored amounts as text instead of numbers or accounting

      Good Afternoon, We have a quarterly billing report that we generate from our Requests. It exports to excel. However if we need to add a formula (something as simple as a sum of the column), it doesn't read the dollar amounts because the export stores
    • Increase Round Robin Scheduler Frequency in Zoho Desk

      Dear Zoho Desk Team, We hope this message finds you well. We would like to request an enhancement to the Round Robin Scheduler in Zoho Desk to better address ticket assignment efficiency. Current Behavior At present, the Round Robin Scheduler operates
    • Automation #6 - Prevent Re-opening of Closed Tickets

      This is a monthly series where we pick some common use cases that have been either discussed or most asked about in our community and explain how they can be achieved using one of the automation capabilities in Zoho Desk. Typically when a customer submits
    • Zoho Desk EU slow/unresponsive

      Has anyone else got issues with ZohoDesk today in the EU? It takes an age to come back and if you do start typing something and try and send or save it looks like it times out. We are also getting this pic. ot I can't see anything on https://status.zoho.eu/
    • EU DC Partial Outage Resolved: A Detailed RCA

      Incident Summary Due to an overload on one of the nodes in the EU DC for Zoho Desk, the system was unable to handle the heavy load, causing a slowdown in requests and resulting in a partial outage for customers with data residing in that node. On May
    • Use Zoho Creator as a source for merge templates in Zoho Writer

      Hello all! We're excited to share that we've enhanced Zoho Creator's integration with Zoho Writer to make this combination even more powerful. You can now use Zoho Creator as a data source for mail merge templates in Zoho Writer. Making more data from
    • Perfomance Management - Zoho People

      Hi team, I am looking for performance management data such as KRA, goals, feedback, appraisals, etc., in Zoho Analytics. However, I am unable to find these metrics while editing the setup. Could you please confirm whether these fields are available in
    • Deprecation of the Zoho OAuth connector

      Hello everyone, At Zoho, we continuously evaluate our integrations to ensure they meet the highest standards of security, reliability, and compliance. As part of these ongoing efforts, we've made the decision to deprecate the Zoho OAuth default connector
    • Good news! Calendar in Zoho CRM gets a face lift

      Dear Customers, We are delighted to unveil the revamped calendar UI in Zoho CRM. With a complete visual overhaul aligned with CRM for Everyone, the calendar now offers a more intuitive and flexible scheduling experience. What’s new? Distinguish activities
    • Zoho Mail iOS app update: Display recipient's nickname on contact suggestion

      Hello everyone! In the most recent version(v3.3.1) of the Zoho Mail iOS app update we have brought in support to display recipient's nickname in contacts suggestion. Please update the app to the latest version directly from the App Store or using the
    • Digest Février - Un résumé de ce qui s'est passé le mois dernier sur Community

      Bonjour chers utilisateurs, Le 26 février, nous avons organisé notre première session Ask the Expert de 2026. Nous sommes heureux de partager que ce fut une session très interactive, avec de nombreuses questions intéressantes posées par nos clients. Si
    • Zoho CRM Case Notes - Share to Customer

      Hi team does anyone know the Api to set a note, against a case, to shared with customer? i cant seem to find it  cheers 
    • Zoho Forms - Feature Request - Past Into Scanning/OCR Field

      Hi Zoho Forms Team, You recently introduced the OCR/Scanning field which I have found great use for with one client who receives work orders as a screenshot from one customer. I want to raise a feature request here which would make that field even more
    • Synching changes to Stripe when changes are made in Zoho Billing

      We have a situation where we have merged customers in Zoho BIlling and then found out later that the payment in Stripe was not updated and still associated with the old customer record. The card gets updated and billed, but that payment is still associated
    • Send out follow-up email in the same thread (threaded conversations in individual emails) from Zoho CRM

      Hi, I'm new to Zoho. I'm trying to send individual emails to my leads one by one. And I'll send out follow up email if I don't hear back from them later. However, instead of sending a new email, I want to reply in the same email thread so that the recipients
    • Depositing funds to account

      Hello, I have been using Quickbooks for many years but am considering moving to Zoho Books so I am currently running through various workflows and am working on the Invoicing aspect. In QB, the process is to create an invoice, receive payment and then
    • Is Zoho Sites still actively being developed?

      Hello, Is Zoho Sites still actively being developed as part of the Zoho ecosystem? I noticed that the What's New page (https://www.zoho.com/sites/whats-new.html) does not show any updates since Q1 2025. We were considering migrating our website from Squarespace
    • FSM integration with Books

      Hi, I have spent a few months working with FSM and have come across a critical gap in the functionality, which I find almost shocking....either that, or I am an idiot. The lack of bi-directional sync between Books and FSM on Sales Orders/ Work Orders
    • web to lead

      Can anyone help me how to create web to lead from zoho marketing automation
    • Introducing Workqueue: your all-in-one view to manage daily work

      Hello all, We’re excited to introduce a major productivity boost to your CRM experience: Workqueue, a dynamic, all-in-one workspace that brings every important sales activity, approval, and follow-up right to your fingertips. What is Workqueue? Sales
    • UI issue with Organize Tabs

      When looking at the organize Tabs window (bellow) you can see that some tabs are grayed out. there is also a "Add Module/Web Tab" button. When looking at this screen it's clear that the grayed out tabs can not be removed from the portal user's screen
    • I created a signup form in Zoho Marketing Automation with the correct field mapping, but the data is not being reflected under Leads in Zoho CRM.

      web to lead from zoho marketing automation
    • Pasted Images not being embedded in custom mail

      Hi, I'm making a custom report by email based on commentaries. I have the email ready, all working great except for images that are being pasted in the commentaries. Zoho deals with them as temp images and so it requires authentication to view them, something
    • Kaizen #232 - Building a Ticket Escalation Mechanism from Zoho CRM

      Howdy, Tech Wizards! Picking up the thread from last week, we will continue our Zoho CRM and Zoho Desk integration. In Kaizen #231 - Embedding Zoho Desk Tickets in Zoho CRM, we built a Related List widget that displays open Zoho Desk tickets within the
    • Inactive License for free account.

      I recently upgraded my Cliq subscription not my team (on the free version), are unable to login to their accounts. The error message received is Inactive License Looks like you have not been covered under the current free plan of users. Please contact
    • Deluge scripts

      Why is there not a search function to make it easier to find the script of interest when modifications are required.
    • Zoho Sheet for Desktop

      Does Zoho plans to develop a Desktop version of Sheet that installs on the computer like was done with Writer?
    • Zoho Books (and other Finance Suite Apps) - Clickable Item Name on Invoices and Reports

      Hi Zoho Books team, It would be really helpful if the Item Name on Reports were clicable to take me to the item. The same on Invoices, often I am looking at an invoice and I want to look at the deails of a product. A link here would be helpful to jump
    • Zoho Books - Payment Gateway - Revolut

      Hi Books Team, My feature request if to include the popular platform Revolut as a payment collection option on invoices in Zoho Books. Please upvote if you are also looking for this option.
    • Searching for an item from within an invoice should show any item containing the string, not just those that begin with the string.

      Hello, I've found myself becoming increasingly frustrated with the limited line-item seach when adding new items to an invoice. The problem is that the characters being typed are only being matched from the beginning of the item name.  This might be usable
    • How to Rank Tables by Row Count in Descending Order

      I am trying to understand the consume of lines that grow up so fast in the last week. Is there any way to create a pivot table or query to get TABLE NAME LINES in descending order?
    • Dealing With One-Time Customers on Zoho Books

      Hello there! I am trying to figure out a way to handle One-Time customers without having to create multiple accounts for every single one on Zoho Books. I understand that I can create a placeholder account called "Walk-In Customer", for example, but I
    • Next Page