> ## Documentation Index
> Fetch the complete documentation index at: https://developers.bottari.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Filters and sorts

> Conditions you can use in filter and sorts for POST /v1/data_sources/{id}/query.

## Combine conditions

A condition is a property plus a condition expression. You can combine them with `and` and `or`, up to 2 levels deep.

```json theme={null}
{
  "filter": {
    "and": [
      {
        "property": "Status",
        "status": {
          "equals": "In progress"
        }
      },
      {
        "or": [
          {
            "property": "Team",
            "select": {
              "equals": "Ops"
            }
          },
          {
            "property": "Team",
            "select": {
              "is_empty": true
            }
          }
        ]
      }
    ]
  }
}
```

`property` accepts a property name or ID. Each array holds up to 100 items, and a request holds up to 1,000 conditions.

## Property conditions

| Filter key | Conditions |
| - | - |
| `title`, `rich_text`, `url`, `email`, `phone_number` | `equals`, `does_not_equal`, `contains`, `does_not_contain`, `starts_with`, `ends_with`, `is_empty`, `is_not_empty` |
| `number` | `equals`, `does_not_equal`, `greater_than`, `less_than`, `greater_than_or_equal_to`, `less_than_or_equal_to`, `is_empty`, `is_not_empty` |
| `checkbox` | `equals`, `does_not_equal` |
| `select`, `status` | `equals`, `does_not_equal`, `is_empty`, `is_not_empty` |
| `multi_select` | `contains`, `does_not_contain`, `is_empty`, `is_not_empty` |
| `date`, `created_time`, `last_edited_time` | `equals`, `before`, `after`, `on_or_before`, `on_or_after`, `is_empty`, `is_not_empty`, `past_week`, `past_month`, `past_year`, `next_week`, `next_month`, `next_year`, `this_week` |
| `people`, `created_by`, `last_edited_by`, `relation` | `contains`, `does_not_contain`, `is_empty`, `is_not_empty` |
| `files` | `is_empty`, `is_not_empty` |
| `unique_id` | `equals`, `does_not_equal`, `greater_than`, `less_than`, `greater_than_or_equal_to`, `less_than_or_equal_to` |
| `formula` | `string`, `checkbox`, `number`, or `date`, each with that type's conditions |
| `rollup` | `any`, `every`, `none`, `number`, `date` |

`button` properties can't be filtered. The response is 400.

A filter key that doesn't match the property returns 400, with these exceptions: `title`, `rich_text`, `url`, `email`, and `phone_number` properties accept the `rich_text` key; `created_by` and `last_edited_by` properties accept the `people` key; and `created_time` and `last_edited_time` properties accept the `date` key.

## Text conditions

* Values are trimmed before comparing, and case is ignored, including for `equals`.
* A value that's empty after trimming matches every row, including `equals: ""`. In `formula.string`, it matches rows whose formula returns text.

## Select conditions

* A value is one name or an array of names. An array matches any of them.
* Names are case-sensitive. An unknown name returns 400 `select option "X" not found for property "P". Available options: ...`
* `status` also accepts the group names `To-do`, `In progress`, and `Complete`, which match every option in that group.
* A single empty string means no condition.

## Date conditions

* A value is ISO 8601 text, or one of `today`, `tomorrow`, `yesterday`, `one_week_ago`, `one_week_from_now`, `one_month_ago`, `one_month_from_now`.
* A value with no time means 00:00 UTC that day. `equals` covers that whole day, and `after` starts from the next day.
* A value with a time and no offset is read as UTC. A date range is compared by its start date.
* The value for conditions like `past_week` is an empty object `{}`. `this_week` is the 7 days from Sunday UTC. `past_week` and `next_week` include today.

```json theme={null}
{ "property": "Due date", "date": { "on_or_after": "today" } }
```

```json theme={null}
{ "property": "Due date", "date": { "next_week": {} } }
```

## People and relation conditions

The value is an ID. `"me"` in `people`, `created_by`, or `last_edited_by` matches nothing, because an integration isn't a person.

To find rows an automation wrote, use `{ "property": "Last edited by", "last_edited_by": { "contains": "00000000-0000-0000-0000-000000000003" } }`.

## Filter by timestamp

You can filter by a row's created or edited time without a property, even in a database with no such property. Comparison uses values truncated to the minute.

```json theme={null}
{
  "filter": {
    "timestamp": "last_edited_time",
    "last_edited_time": { "on_or_after": "2026-01-15T00:00:00Z" }
  }
}
```

## Sorts

`sorts` is an array of up to 100 items, applied in order.

```json theme={null}
{
  "sorts": [
    {
      "property": "Due date",
      "direction": "ascending"
    },
    {
      "timestamp": "created_time",
      "direction": "descending"
    }
  ]
}
```

* `direction` is `ascending` or `descending`.
* `timestamp` is `created_time` or `last_edited_time`. Sorting uses the stored time, not the time truncated to the minute.
* With no sort, or when values tie, the most recently created rows come first.
* Empty values come last in both directions.
* Text ignores case, and on a tie, uppercase comes first.
* `select` sorts by option order.

## Performance

With a filter or sort, the server reads every visible row in the database to compute the result. Without them, it reads only as many rows as it needs.

## Common errors

| Message | Cause |
| - | - |
| `Could not find property with name or id: X` | The filter's property doesn't exist. |
| `Could not find sort property with name or id: X` | The sort's property doesn't exist. |
| `The property type in the database does not match the property type of the filter provided: ...` | The property and filter key differ. |
| `body failed validation. Fix one: ...` | Unknown condition. Each line lists an accepted condition. |
| `... should be nested at most 2 levels deep.` | `and` and `or` are nested more than 2 levels. |
| `body failed validation: body.filter should have at most 1000 conditions in total.` | There are over 1,000 conditions. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.