Email SupportCall Us Go to Close

Advanced Filtering


Tutorial

The Advanced Filtering API allows you to use arbitrary filters to find Leads or Contacts with specific attributes.

To use the Advanced Filtering API, make a POST request to /api/v1/data/search/ with your JSON filters in the request body. Let’s consider a simple example of finding all Contacts with CEOs you have on file in Close. The example POST request data with the query would look like this:

{
  "query": {
    "type": "and",
    "queries": [
      {
        "type": "object_type",
        "object_type": "contact"
      },
      {
        "type": "field_condition",
        "field": {
          "type": "regular_field",
          "object_type": "contact",
          "field_name": "title"
        },
        "condition": {
          "type": "text",
          "mode": "full_words",
          "value": "CEO"
        }
      }
    ]
  }
}

The request consists of a top level "and" query which combines two constraints on what would be returned:

  • An object_type query that tells the API that you’re interested in finding Contacts.
  • A field_condition query that asks for Contacts that have a title value containing a word "CEO".

Every query has a "type" that distinguishes the way you filter through objects.

The first part of the example query is "type": "object_type". It specifies that you only want objects of type Contact returned in the response.

The second part of the example query is "field_condition". Field condition queries specify the field you want to match against (in this case "title"), and the values you want to match it to (i.e. "CEO").

The field in this example is a "regular_field" named "title" on a "contact" object. There can be other kinds of fields as well, like Contact Custom Fields.

The field condition here is a simple "text" condition. It matches against the text "CEO", so any contact that have a title containing word "CEO" will be returned.

Here’s an example response:

{
  "data": [
    {
      "__object_type": "contact",
      "id": "cont_PtENYt1P6sRWkcnqoGnIj6hcZammGPdVbTXJWqdTvpV"
    },
    {
      "__object_type": "contact",
      "id": "cont_HjTcJFiNli2AKf5fioygQDVSpA9nJILI9SkKv3nBk0A"
    },
    {
      "__object_type": "contact",
      "id": "cont_LXQqW8mvD0BbCR6qhRPbzPZF7gCbXQIw6GD8vqKipss"
    }
  ],
  "cursor": null
}

More complex query

A more complicated example would be finding CEOs of SaaS companies that have at least one email address.

{
  "query": {
    "type": "and"
    "queries": [
      {
        "type": "object_type",
        "object_type": "contact"
      },
      {
        "type": "field_condition",
        "field": {
          "type": "regular_field",
          "object_type": "contact",
          "field_name": "emails_count"
        },
        "condition": {
          "type": "number_range",
          "gt": 1
        }
      },
      {
        "type": "field_condition",
        "field": {
          "type": "regular_field",
          "object_type": "contact",
          "field_name": "title"
        },
        "condition": {
          "type": "text",
          "mode": "full_words",
          "value": "CEO"
        }
      },
      {
        "type": "has_related",
        "this_object_type": "contact",
        "related_object_type": "lead",
        "related_query": {
          "type": "field_condition",
          "field": {
            "type": "custom_field",
            "custom_field_id": "cf_B57sofuEB7OBneH86SJctTr7hfiAiQcgkGRbAQfha0Z"
          },
          "condition": {
            "type": "term",
            "values": ["Software as a service"]
          },
          "negate": false
        }
      }
    ]
  }
}

In this query you can see several new concepts:

  • A different condition for a field that represents a "number_range" as opposed to just textual match. gt: 1 means greater than one.
  • A field type "custom_field" that references a Lead Custom Field by its id.
  • A "has_related" query that lets you match on data from a different related object rather than the one you want returned, in this case a Lead.

If the examples here don't cover your use case, check out Visual Query Builder for help with building a query structure tailored to your needs.

Visual Query Builder

Instead of composing the JSON query structure by hand, you can build your query visually on the Leads Page or Contacts page in Close app, and then copy the JSON query structure by clicking the triple-dot menu on the top right and selecting "Copy Filters".

Advanced Filtering

Output control

By default, the Advanced Filtering API only returns the IDs of the matched objects to improve performance. If you want to get more the data for the object, you will need to pass a _fields object to specify which fields you want returned for the object you're filtering for. For example:

{
  "query": ...,
  "_fields": {
    "contact": ["id", "name", "title"]
  }
}

The fields are exactly the same as ones in REST API for corresponding object types.

This results in the following:

{
  "data": [
    {
      "__object_type": "contact",
      "id": "cont_HjTcJFiNli2AKf5fioygQDVSpA9nJILI9SkKv3nBk0A",
      "name": "Bruce Wayne",
      "title": "The Dark Knight"
    },
    {
      "__object_type": "contact",
      "id": "cont_LXQqW8mvD0BbCR6qhRPbzPZF7gCbXQIw6GD8vqKipss",
      "name": "Steli Efti",
      "title": "CEO & Co-Founder"
    }
  ]
}

Results limit

If you want to only get first N results, you can specify a results limit.

Limiting results might be useful if you have 1000 objects but you want to act only on the first 100.

    {
      "query": ...,
      "results_limit": 100,
    }

Note that this limits the number returned in the overall result set and is separate from pagination.

Results count

Sometimes you might want to know how many results there are. For performance, they are not included by default. Request them explicitly with include_counts flag.

{
  "query": ...,
  "include_counts": true,
}

In the response, you'll get a count object.

{
  "data": [...],
  "count": {
    "limited": 3,
    "total": 3
  }
}

If you don't need the actual results but only care about the number, set the results_limit to zero.

{
  "query": ...,
  "include_counts": true,
  "results_limit": 0
}

In the response, the total count is the number you're interested in.

{
  "data": [],
  "count": {
    "limited": 0,
    "total": 3
  }
}

Sorting

You can sort the objects returned using a sort field whose value is a list of fields and their respective sort directions. In this example, we sort Contacts by title.

{
  "query": ...,
  "sort": [
    {
      "direction": "asc",
      "field": {
        "object_type": "contact",
        "type": "regular_field",
        "field_name": "title"
      }
    }
  ]
}

Sorting can be in either asc or desc direction.

Only numbers, dates, and text fields that belong directly to an object can be used for sorting. References/ID fields or fields from other objects cannot be used for sorting.

Pagination

A single request only returns a page worth of results. You can control how many results you want on one page using the _limit field, and ask for the next pages by sending the cursor field populated with a value from a previous response.

{
  "query": ...,
  "_limit": 10,
  "cursor": "<random string>" // <- cursor value from previous response
}

When the cursor value you're received in your most recent response is null, it means that you've reached the last page and no more results are available.

Cursors expire after 30 seconds; if you try to use an expired cursor you'll get an Expired cursor error.

We have a hard limit of 10,000 objects when paginating. If you need to paginate for more than 10,000 results, update your query to return smaller batches of objects. A popular method to do this would be to use the date_created field as a range, and make multiple requests while incrementing or decrementing the date.

Note: When paginating, be sure to use a sort that won’t change often like date_created, so that you won’t miss any results if the sort order changes between individual page requests.

Available query types

ID query

Matches a single object directly by its ID.

{
  "type": "id",
  "value": "cont_abcdef"
}

Object type

Specify the object type that the API should return (i.e. contact or lead)

{
  "object_type": "contact",
  "type": "object_type"
}

Text query

Filter for text across most text-like fields on an object.

{
  "type": "text",
  "value": "Bruce",
  "mode": "full_words"
}

The two available modes are:

  • full_words searches all specified words anywhere in the text but disregards position of the words.
  • phrase searches the words in specified order next to each other.

A has_related query lets you filter for a Contact based on data of the Lead they belong to, as well as individual email address and phone number records.

{
"type": "has_related",
"this_object_type": "contact",
"related_object_type": "lead",
"related_query": {
  "field": {
    "object_type": "lead",
    "type": "regular_field",
    "field_name": "source"
  },
  "condition": ...,
}

Query negation

All queries support negation. By specifying "negate": true, everything that matches the query will not be returned, and everything that doesn't match the query will.

Combining queries with AND/OR

To get objects that match multiple conditions all at once (AND) or at least one of the conditions, use and/or queries.

{
  "type": "and",
  "queries": [
    query1, query2, query3
  ]
}
{
  "type": "or",
  "queries": [
    query1, query2, query3
  ]
}

Field Value Condition Query

Match Contacts where the value in a specific field fits the Field Condition.

{
  "type": "field_condition",
  "field": {
    "type": "regular_field",
    "object_type": "contact",
    "field_name": "emails_count"
  },
  "condition": {
    "type": "number_range",
    "gt": 1
  }
}

There can be two field types: regular fields and custom fields.

{
  "field": {
    "type": "regular_field",
    "object_type": "contact",
    "field_name": "name"
  }
}

Custom fields:

{
  "field": {
    "type": "custom_field",
    "custom_field_id": "cf_abcdef"
  }
}

If you're unsure what query type to use, try the Visual Query Builder.

Field Conditions

Field Conditions are conditions that apply to individual field values as opposed to entire object.

Boolean

A yes/no value.

{
  "type": "boolean",
  "value": true
}

Current user

Represents a reference to a currently authenticated user, also known as "me". Use with fields like created_by.

{ "type": "current_user" }

Exists

Matches if the field is set to some value. Null or missing fields do not match.

{ "type": "exists" }

Text

Filters for text in the field value. Similar to Text query.

{
  "type": "text",
  "value": "Bruce",
  "mode": "full_words"
}

Term

Matches one or multiple specific Enumeration or Choices values.

{
  "type": "term",
  "values": ["draft", "sent"]
}

Reference

Matches specific object IDs in reference fields like User ID or Lead Status IDs.

{
  "type": "reference",
  "reference_type": "user",
  "object_ids": ["user_abc", "user_def"]
}

Most commonly used fields

For Contact: Object type: contact

  • created_by: User ID
  • date_created: datetime
  • date_updated: datetime
  • emails_count: integer
  • lead_id: Lead ID
  • phones_count: integer
  • title: text
  • updated_by: User ID
  • urls_count: integer

For Lead: Object type: lead

  • addresses_count: integer
  • all_urls_count: integer
  • created_by: User ID
  • date_created: datetime
  • date_updated: datetime
  • description: text
  • display_name: text
  • status_id: Lead Status ID
  • updated_by: User ID
  • url: url

For Contact Phones: Object type: contact_phone

  • type: text
  • phone: text

For Contact Emails: Object type: contact_email

  • type: text
  • email: email address

For Contact URLs: Object type: contact_url

  • type: text
  • url: url

For Addresses: Object type: address

  • address_1: text
  • address_2: text
  • city: text
  • country: text
  • location: text
  • state: text
  • zipcode: text