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

# Search Records

> Search for records in the specified table based on filter, sort, and pagination criteria.

## Filtering Guide

The Search Records endpoint lets you filter, sort, and paginate records using a JSON request body. All three top-level fields (`filter`, `sorting`, `paging`) are optional.

```json theme={null}
{
  "paging": {
    "offset": 0,
    "limit": 50
  },
  "sorting": [
    { "sortingField": "field-uuid", "sortType": "ASC" }
  ],
  "filter": {
    "condition": { ... }
  }
}
```

### Filter Condition Types

Filters are passed in `filter.condition`. There are four types, determined by the operator.

#### Binary Conditions

Compare a field to a value:

```json theme={null}
{
  "operator": "CONTAINS",
  "leftSide": "field-uuid",
  "rightSide": "search term"
}
```

| Operator                 | Description                   | Right Side     |
| ------------------------ | ----------------------------- | -------------- |
| `IS`                     | Equals                        | single value   |
| `IS_NOT`                 | Not equals                    | single value   |
| `GREATER_THAN`           | Greater than                  | number or date |
| `GREATER_THAN_OR_EQUALS` | Greater than or equal         | number or date |
| `LESS_THAN`              | Less than                     | number or date |
| `LESS_THAN_OR_EQUALS`    | Less than or equal            | number or date |
| `CONTAINS`               | String contains               | string         |
| `DOES_NOT_CONTAIN`       | String does not contain       | string         |
| `STARTS_WITH`            | String starts with            | string         |
| `DOES_NOT_START_WITH`    | String does not start with    | string         |
| `ENDS_WITH`              | String ends with              | string         |
| `DOES_NOT_END_WITH`      | String does not end with      | string         |
| `IS_ONE_OF`              | Value is one of               | array          |
| `IS_NOT_ONE_OF`          | Value is none of              | array          |
| `HAS_ANY_OF`             | Multi-value field has any of  | array          |
| `HAS_ALL_OF`             | Multi-value field has all of  | array          |
| `HAS_NONE_OF`            | Multi-value field has none of | array          |

#### Unary Conditions

Check whether a field is empty — no `rightSide` needed:

```json theme={null}
{
  "operator": "IS_EMPTY",
  "leftSide": "field-uuid"
}
```

| Operator       | Description        |
| -------------- | ------------------ |
| `IS_EMPTY`     | Field has no value |
| `IS_NOT_EMPTY` | Field has a value  |

#### Ternary Conditions

Range-based checks with `lowerBound` and `upperBound`:

```json theme={null}
{
  "operator": "IS_BETWEEN",
  "leftSide": "field-uuid",
  "lowerBound": 10,
  "upperBound": 100
}
```

| Operator         | Description                         |
| ---------------- | ----------------------------------- |
| `IS_BETWEEN`     | Value is between bounds (inclusive) |
| `IS_NOT_BETWEEN` | Value is outside bounds             |
| `IS_WITHIN`      | Date is within a relative range     |
| `IS_NOT_WITHIN`  | Date is outside a relative range    |

#### Composite Conditions

Combine multiple conditions with `AND` or `OR`:

```json theme={null}
{
  "operator": "AND",
  "conditions": [
    { "operator": "CONTAINS", "leftSide": "name-field", "rightSide": "acme" },
    { "operator": "GREATER_THAN", "leftSide": "amount-field", "rightSide": 1000 },
    {
      "operator": "OR",
      "conditions": [
        { "operator": "IS", "leftSide": "status-field", "rightSide": "active" },
        { "operator": "IS_EMPTY", "leftSide": "archived-field" }
      ]
    }
  ]
}
```

### Relative Date Values

For date fields, you can use relative date strings instead of fixed ISO 8601 dates:

| Format                          | Examples                                                                                                                                                                                                                                                                     |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PREDEFINED:<token>`            | `PREDEFINED:TODAY`, `PREDEFINED:YESTERDAY`, `PREDEFINED:TOMORROW`, `PREDEFINED:THIS_WEEK`, `PREDEFINED:LAST_WEEK`, `PREDEFINED:THIS_MONTH`, `PREDEFINED:THIS_YEAR`, `PREDEFINED:NEXT_MONTH`, `PREDEFINED:PAST_MONTH`, `PREDEFINED:7_DAYS_AGO`, `PREDEFINED:30_DAYS_FROM_NOW` |
| `RELATIVE_DATE:<period>:<unit>` | `RELATIVE_DATE:THIS:WEEK`, `RELATIVE_DATE:PAST:MONTH`, `RELATIVE_DATE:NEXT:QUARTER`, `RELATIVE_DATE:PAST:7` (7 days ago)                                                                                                                                                     |
| ISO 8601                        | `"2025-05-21"`, `"2025-05-21T20:00:00.000Z"`                                                                                                                                                                                                                                 |

### Complete Example

Find active contacts named "acme" with an amount over 1000, created this month:

```json theme={null}
{
  "paging": { "offset": 0, "limit": 50 },
  "sorting": [
    { "sortingField": "amount-field-uuid", "sortType": "DESC" }
  ],
  "filter": {
    "condition": {
      "operator": "AND",
      "conditions": [
        { "operator": "CONTAINS", "leftSide": "name-field-uuid", "rightSide": "acme" },
        { "operator": "GREATER_THAN", "leftSide": "amount-field-uuid", "rightSide": 1000 },
        { "operator": "IS", "leftSide": "status-field-uuid", "rightSide": "active" },
        { "operator": "IS_WITHIN", "leftSide": "created-at-field-uuid", "lowerBound": "RELATIVE_DATE:THIS:MONTH", "upperBound": "PREDEFINED:TODAY" }
      ]
    }
  }
}
```


## OpenAPI

````yaml openapi.yaml POST /databases/{databaseId}/tables/{tableId}/records/search
openapi: 3.0.0
info:
  title: Softr Database API
  description: >
    Public API to communicate with Softr Databases. The API follows REST
    semantics and uses JSON to encode objects.


    **Rate Limiting:**

    All calls are subject to rate limiting. On exceeding the limit, you will
    receive a 429 HTTP Response Status Code.

    - **Reads** (GET, POST /search): 40 requests/second per token.

    - **Writes** (POST, PUT, PATCH, DELETE): 30 requests/second per token.
  version: 1.0.0
servers:
  - url: https://tables-api.softr.io/api/v1
    description: Production Server
security:
  - ApiKeyAuth: []
tags:
  - name: Databases
    description: Operations related to Softr databases.
  - name: Tables
    description: Operations related to tables within a database.
  - name: Table Fields
    description: Operations to manage fields within a table.
  - name: Records
    description: Operations to manage records (rows) within a table.
paths:
  /databases/{databaseId}/tables/{tableId}/records/search:
    parameters:
      - name: databaseId
        in: path
        required: true
        schema:
          type: string
      - name: tableId
        in: path
        required: true
        schema:
          type: string
    post:
      tags:
        - Records
      summary: Search Records
      description: >-
        Search for records in the specified table based on filter, sort, and
        pagination criteria.
      operationId: searchRecords
      parameters:
        - name: fieldNames
          in: query
          description: >-
            If true, use field names as keys in the fields object instead of
            field IDs.
          schema:
            type: boolean
            default: false
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                filter:
                  type: object
                  properties:
                    condition:
                      $ref: '#/components/schemas/FilterCondition'
                sort:
                  type: array
                  items:
                    type: object
                    properties:
                      sortingField:
                        type: string
                        description: The Field ID to apply the sorting on
                      sortType:
                        type: string
                        enum:
                          - ASC
                          - DESC
                paging:
                  type: object
                  properties:
                    offset:
                      type: integer
                      default: 0
                    limit:
                      type: integer
                      default: 10
      responses:
        '200':
          description: A paginated list of matching records.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Record'
                  metadata:
                    $ref: '#/components/schemas/RecordListMetadata'
        '429':
          description: Rate limit exceeded.
components:
  schemas:
    FilterCondition:
      type: object
      description: >-
        A condition for filtering records. Text-related conditions are
        case-insensitive.
      properties:
        operator:
          type: string
          enum:
            - AND
            - OR
            - IS_EMPTY
            - IS_NOT_EMPTY
            - IS_BETWEEN
            - IS_NOT_BETWEEN
            - IS_WITHIN
            - IS_NOT_WITHIN
            - IS
            - IS_NOT
            - GREATER_THAN
            - GREATER_THAN_OR_EQUALS
            - LESS_THAN
            - LESS_THAN_OR_EQUALS
            - CONTAINS
            - DOES_NOT_CONTAIN
            - STARTS_WITH
            - DOES_NOT_START_WITH
            - ENDS_WITH
            - DOES_NOT_END_WITH
            - IS_ONE_OF
            - IS_NOT_ONE_OF
            - HAS_ANY_OF
            - HAS_ALL_OF
            - HAS_NONE_OF
        leftSide:
          type: string
          description: The Field ID to apply the operator on.
        rightSide:
          description: >-
            The value to compare against. Can be a string, number, or an array
            for operators like IS_ONE_OF.
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: array
              items:
                type: string
        lowerBound:
          type: string
          description: Lower bound for IS_BETWEEN operator.
        upperBound:
          type: string
          description: Upper bound for IS_BETWEEN operator.
        conditions:
          type: array
          description: Used for AND/OR operators to nest conditions.
          items:
            $ref: '#/components/schemas/FilterCondition'
    Record:
      type: object
      properties:
        id:
          type: string
        tableId:
          type: string
        fields:
          type: object
          description: A map of Field IDs to their values for this record.
          additionalProperties: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    RecordListMetadata:
      type: object
      properties:
        offset:
          type: integer
        limit:
          type: integer
        total:
          type: integer
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Softr-Api-Key

````