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 #197: Frequently Asked Questions on GraphQL APIs

      🎊 Nearing 200th Kaizen Post – We want to hear from you! 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 #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.
    • Celebrating 200 posts of Kaizen! Share your ideas for the milestone post

      Hello Developers, We launched the Kaizen series in 2019 to share helpful content to support your Zoho CRM development journey. Staying true to its spirit—Kaizen Series: Continuous Improvement for Developer Experience—we've shared everything from FAQs
    • Kaizen #193: Creating different fields in Zoho CRM through API

      🎊 Nearing 200th Kaizen Post – We want to hear from you! 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.
    • Client Script | Update - Introducing Commands in Client Script!

      Have you ever wished you could trigger Client Script from contexts other than just the supported pages and events? Have you ever wanted to leverage the advantage of Client Script at your finger tip? Discover the power of Client Script - Commands! Commands
    • Recent Topics

    • Cannot log in to IMAP account as of last night

      Hey I've been using MFA with an authenticator for a while and have had to use application passwords for Outlook and Edison Mail on my Android devices. Last night the app passwords started to be rejected on my Android devices so I created new ones for
    • Low Stock View

      We use the Low Stock view frequently as a guide to inform us when to reorder items, but the view is misleading because it does not take into account Purchase Orders that have already been raised. Unless you are aware and check item by item, this can lead
    • Improve History Feature in Zoho Inventory

      At present there is a "history" tab on Zoho Inventory Items, however this only shows a date and time stamp along with the users name. It doesn't say what was changed. What is the value of this if you can't see what was changed? My Ideal is to include
    • Payroll and BAS ( Australian tax report format )

      Hello , I am evaluating Zoho Books and I find the interface very intuitive and straight forward. My company is currently using Quickbooks Premier the Australian version. Before we can consider moving the service we would need to have the following addressed : 1.Payroll 2.BAS ( business activity statement ) for tax purposes 3.Some form of local backup and possible export of data to a widely accepted format. Regards Codrin Mitin
    • Show backordered items on packing slip

      We send out a lot of large orders, and often there are one or two things backordered. How can I fix the packing slips to show quantity ordered  & quantity packed There should also be the ability to "ship" 0 of an item so the receiver knows that things
    • Document | Files

      The vendor "Partial matches" still not fixed here after years of putting up with having to select most vendors manually ! ( again, the banking "Transaction rules" would solve a lot of these issues ) Some unwanted, irrelevant pdf's also arrive. It would
    • books+POS+ tap to pay+ stripe

      So in the UK we now have tap to pay with stripe. So we can use the stripe app as a POS terminal. Brilliant news. Can we hope that the Books App might add this feature ASAP. It would be great to have one system rather than using the not very good Square
    • Canvas Form View - Client Script Page on load - Url params not working

      We have a custom module with a canvas page for form view. We have a button in Customers module to open this canvas page using invokeurl function. We send few parameters as in the URL as query parameters. https://crm.zoho.in/crm/orgxxxxxxxx/tab/CustomModule12/create/canvas/64333200000261xxxx?layoutId=643332000002605001&c=${Customers.Customer
    • Sending Recruit SMS's to Zoho Cliq - Or tracking in the Messages module in Recruit?

      Is there any way to send SMS Gateway messages in Recruit to ZOho Cliq? We use 2-way SMS massages a lot in Zoho Recruit to speed up communication with Candidates. However the only way to keep track of received SMS's is by keeping a look out for the Email
    • This mobile number has been marked spam. Please contact support.

      Hi Support, Can you tell me why number was marked as spam. I have having difficult to add my number as you keep requesting i must use it. My number is +63....163 Or is Zoho company excluding Philippines from their services?
    • Zohomail does not support additional fields in mailto links

      Hello, I set up Zohomail as default composer in Firefox according to manual here: https://www.zoho.com/mail/help/defaultcomposer.html#alink2 Later, I wanted to use this functionality to answer in a Linux mailing list thread using mailto link: mailto:xxxxx@kernel.org?In-Reply-To=%3C727o0521-q24p-s0qq-66n0-sn436rpqqr1p@example.com%3E&amp;Cc=xxxxx%example.org&amp;Subject=Re%3A%20%5BPATCH%20v2%28%29
    • Is it possible to set a customer context variable in Zobot

      Hi, I want to use a context variable to route users down different paths in my Zobot chat flow. I know I can do this when the user enters data. But I want to know if I can use a variable that is 'hard coded' on the card, that the user is unaware of. Use
    • Cannot change Blog Title

      There is nowhere to change the blog title. You can change the blog URL but that is making no difference to the text "Enter Your Post Title" am I missing something?
    • Pending Sales Order Reports

      Pending sale order report is available for any single customer, Individual report is available after 3-4 clicks but consolidated list is needed to know the status each item. please help me.
    • Kaizen #203 - Answering Your Questions | Handling API Limits and Error Responses

      Hi Everyone, Welcome back to yet another post in the Kaizen Series! We appreciate your keen participation in the 200th milestone celebration of the Kaizen series. We will continue to answer the queries we received through the feedback. When working with
    • How to verify website ownership with google search console

      Hi, I am having a free .in domain provided by Zoho I have created a website on it now I want to verify my ownership to google webmaster. Can you please help me how to do that.
    • Kaizen #89 - Color Coding using Client Script

      Hello everyone! Welcome back to another exciting Kaizen post. Today let us see how you can apply color codes to the List and Detail Pages of Zoho CRM using Client Script. Need for color code in Zoho CRM When you mark things with different colors as a
    • "SPF record exceed the allowed limit of 10"

      Hi, I was wondering if there were a "universal Zoho SPF record" that would allow all my configured Zoho services that email things to meet this limitation ? or do I have to have an entry for mail, com, billing, etc?
    • Zoho Mail android app update: Block & reject future emails, Report phishing, Spam alerts, Suspicious URL detection

      Hello everyone! In the most recent Zoho Mail Android app update, we have brought in support for the following features: Block & reject future emails Report Phishing Spam alerts in the mail details screen. Suspicious URL detection. Block & reject future
    • What is Resolution Time in Business Hours

      HI, What is the formula used to find the total time spent by an agent on a particular ticket? How is Resolution Time in Business Hours calculated in Zohodesk? As we need to find out the time spent on the ticket's solution by an agent we seek your assistance
    • Animated GIF Images in Chat

      I know this seems to be a small feature request but in a recent Cliq update Zoho disabled autoplay for animated GIFs posted in Cliq conversations. We think this was not a good change. In our organization, animated GIFs in a chat play a role in expressing
    • I can't seem to login in to Mail Apps of MacOS /IOS

      Hi, i'm having trouble in signing in to mail apps from IOS. It's always come back to wrong passwords. But i already changed my password like 3 times. But still it says wrong credentials
    • Profile Image Showing Incorrectly in Zoho Mail

      Hi everyone, I’m facing a serious issue with Zoho Mail. The profile image showing for my email address is incorrect — it’s not my image. When I send an email to my Gmail account, it displays someone else’s image. This looks very suspicious and can make
    • I need access to my old email

      I need access to my old email bromzeman@zoho.com to verify it for forwarding. I can’t access the inbox to get the confirmation code. Please assist with recovery or forwarding. as you might already know, they made alot of the email addresses to have that
    • Set Defaults for Email Groups

      Is there a way to set defaults for all of the groups that I establish as a moderator? For example, I want every group I establish to have the following settings: 1. Moderator is <user> 2. User is <user>, <user> 3. Notifications for new group turned
    • LOGS FOR RECEIVED MAIL ( READ DATE & TIME)

      In Zoho mail we can extract the read logs of received mails so that we can see when we have read the mail and at what time & we have read it.
    • Domain verification probem

      Hello, i use a domain from Namecheap with hosting from Cinfu when i try to verify my domain on zoho i get "TXT Verification failure" i even tried the HTML verification and the code appears but also giving me the verification failure error.
    • Switching to Cloudflare email routing from Zoho Mail

      Hello, I'm currently working on migrating from Zoho Mail to Cloudflare's email routing solution. This requires changing MX and TXT records for our custom domain - when we do this, will our users still be able to log into their accounts and access an archived
    • Un Subscription Button

      How can i Add the Un Subscription Button in Zoho mail
    • Documents unable view and need to downlad

      I can't view .doc files in Zoho mail unless I download them, but I can view PDF files without downloading.
    • we encountered a problem while sending your email. please try again later.

    • Adding and removing people from groups

      We're having problems adding people to a group. Apparently Zoho has one email address and will not recognize a different email address.
    • MAIL SEARCH IS NOT WORKING

      THIS ISSUE HAS BEEN BUGGING ME SINCE MORNING, PLEASE RESOLVE THIS AT THE EARLIEST
    • URL Parameter on Help Center SIgn in link

      Is it possible to add a url parameter to the sign in link on the Help Center?
    • migrating from HelpScout

      I am attempting to import a conversation file from helpscout into desk and am receiving size errors. What is the current file size restriction. Does anyone have any tips for a successful migration?
    • Layout Rules Don't Apply To Blueprints

      Hi Zoho the conditional layout rules for fields and making fields required don't work well with with Blueprints if those same fields are called DURING a Blueprint. Example. I have field A that is used in layout rule. If value of field A is "1" it is supposed to show and make required field B. If the value to field A is "2" it is supposed to show and make required field C. Now I have a Blueprint that says when last stage moves to "Closed," during the transition, the agent must fill out field A. Now
    • Article Name Sorting in Zoho Desk Knowledge Base (agent / admin side)

      Dear Zoho Desk Support, We are writing to request an enhancement to the Knowledge Base management feature within Zoho Desk. Currently, there is no option to sort articles by their name, which significantly hinders efficient article management, especially
    • How to parse JSON data with SQL in Zoho Analytics?

      Hi all, I have a column with JSON data. I want to show this column in a chart, but it is very messy, and no JSON parsing function is supported on Zoho Analytics. data example: {"id": 5, "status": "false", "date": "15/10/22"} what I want to do in SQL is
    • Ability to turn off "Would you like this amount to be reflected in the Payment field?" message

      Team, Is there any way to turn off the message" Would you like this amount to be reflected in the Payment field?" when I make a payment? This is so annoying. This happens EVERY TIME I put an amount in the Payment Made field.
    • Add an Equation Field (Or update the Formula Field)

      Hi, I would like to be able to have one field as a Text Field with QR Code, and then have multiple Equation/Formula Fields that then take parts of that fields data with LEFT, MID, RIGHT, REGEX, etc. Thanks Dan
    • Next Page