---
title: Memory Filters
description: Query and retrieve memories with powerful filtering capabilities. Filter by users, agents, content, time ranges, and more.
---

> Memory filters provide a flexible way to query and retrieve specific memories from your memory store. You can filter by users, agents, content categories, time ranges, and combine multiple conditions using logical operators.

## When to use filters

When working with large-scale memory stores, you need precise control over which memories to retrieve. Filters help you:

* **Isolate user data**: Retrieve memories for specific users while maintaining privacy
* **Debug and audit**: Export specific memory subsets for analysis
* **Target content**: Find memories with specific categories or metadata
* **Time-based queries**: Retrieve memories within specific date ranges
* **Performance optimization**: Reduce query complexity by pre-filtering

<Callout type="info" icon="info-circle" color="#7A5DFF">
Filters were introduced in v1.0.0 to provide precise control over memory retrieval.
</Callout>

## Filter structure

Filters use a nested JSON structure with logical operators at the root:

```python
# Basic structure
{
    "AND": [  # or "OR", "NOT"
        { "field": "value" },
        { "field": { "operator": "value" } }
    ]
}
```

## Available fields and operators

### Entity fields
| Field | Operators | Example |
|-------|-----------|---------|
| `user_id` | `eq`, `ne`, `in`, `*` | `{"user_id": "user_123"}` |
| `agent_id` | `eq`, `ne`, `in`, `*` | `{"agent_id": "*"}` |
| `app_id` | `eq`, `ne`, `in`, `*` | `{"app_id": {"in": ["app1", "app2"]}}` |
| `run_id` | `eq`, `ne`, `in`, `*` | `{"run_id": "*"}` |

### Time fields
| Field | Operators | Example |
|-------|-----------|---------|
| `created_at` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"created_at": {"gte": "2024-01-01"}}` |
| `updated_at` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"updated_at": {"lt": "2024-12-31"}}` |
| `timestamp` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` | `{"timestamp": {"gt": "2024-01-01"}}` |

### Content fields
| Field | Operators | Example |
|-------|-----------|---------|
| `categories` | `eq`, `ne`, `in`, `contains` | `{"categories": {"in": ["finance"]}}` |
| `metadata` | `eq`, `ne`, `contains` | `{"metadata": {"key": "value"}}` |
| `keywords` | `contains`, `icontains` | `{"keywords": {"icontains": "invoice"}}` |

### Special fields
| Field | Operators | Example |
|-------|-----------|---------|
| `memory_ids` | `in` | `{"memory_ids": ["id1", "id2"]}` |

<Callout type="warning" icon="exclamation-triangle" color="#F7B731">
The `*` wildcard matches any non-null value. Records with null values for that field are excluded.
</Callout>

<Callout type="info" icon="keyboard" color="#00A8FF">
Use operator keywords exactly as shown (`eq`, `ne`, `gte`, etc.). SQL-style symbols such as `>=` or `!=` are rejected by the Platform API.
</Callout>

## Common filter patterns

Use these ready-made filters to target typical retrieval scenarios without rebuilding logic from scratch.

<AccordionGroup>
  <Accordion title="Single user">
    ```python
    # Narrow to one user's memories
    filters = {"AND": [{"user_id": "user_123"}]}
    memories = client.get_all(filters=filters)
    ```
  </Accordion>

  <Accordion title="All users">
    ```python
    # Wildcard skips null user_id entries
    filters = {"AND": [{"user_id": "*"}]}
    memories = client.get_all(filters=filters)
    ```
  </Accordion>

  <Accordion title="User across all runs">
    ```python
    # Pair a user filter with a run wildcard
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"run_id": "*"}
        ]
    }
    memories = client.get_all(filters=filters)
    ```
  </Accordion>
</AccordionGroup>

<Callout type="warning" icon="exclamation-triangle" color="#E74C3C">
Metadata filters only support bare values/`eq`, `contains`, and `ne`. Operators such as `in`, `gt`, or `lt` trigger a `FilterValidationError`. For multi-value checks, wrap multiple equality clauses in `OR`.
</Callout>

```python
# Multi-value metadata workaround
filters = {
    "OR": [
        {"metadata": {"type": "semantic"}},
        {"metadata": {"type": "episodic"}}
    ]
}
```

### Content search

Find memories containing specific text, categories, or metadata values.

<AccordionGroup>
  <Accordion title="Text search">
    ```python
    # Case-insensitive match
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"keywords": {"icontains": "pizza"}}
        ]
    }

    # Case-sensitive match
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"keywords": {"contains": "Invoice_2024"}}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Categories">
    ```python
    # Match against category list
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"categories": {"in": ["finance", "health"]}}
        ]
    }

    # Partial category match
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"categories": {"contains": "finance"}}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Metadata">
    ```python
    # Pin to a metadata attribute
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"metadata": {"source": "email"}}
        ]
    }
    ```
  </Accordion>
</AccordionGroup>

### Time-based filtering

Retrieve memories within specific date ranges using time operators.

<AccordionGroup>
  <Accordion title="Date range">
    ```python
    # Created in January 2024
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"created_at": {"gte": "2024-01-01T00:00:00Z"}},
            {"created_at": {"lt": "2024-02-01T00:00:00Z"}}
        ]
    }

    # Updated recently
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"updated_at": {"gte": "2024-12-01T00:00:00Z"}}
        ]
    }
    ```
  </Accordion>
</AccordionGroup>

### Multiple criteria

Combine various filters for complex queries across different dimensions.

<AccordionGroup>
  <Accordion title="Multiple users">
    ```python
    # Expand scope to a short user list
    filters = {
        "AND": [
            {"user_id": {"in": ["user_1", "user_2", "user_3"]}}
        ]
    }
    ```
  </Accordion>

  <Accordion title="OR logic">
    ```python
    # Return matches on either condition
    filters = {
        "OR": [
            {"user_id": "user_123"},
            {"run_id": "run_456"}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Exclude categories">
    ```python
    # Wrap negative logic with NOT
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"NOT": {
                "categories": {"in": ["spam", "test"]}
            }}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Specific memory IDs">
    ```python
    # Fetch a fixed set of memory IDs
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"memory_ids": ["mem_1", "mem_2", "mem_3"]}
        ]
    }
    ```
  </Accordion>

  <Accordion title="All entities populated (single entity scope)">
    ```python
    # Require user_id plus non-null run/app IDs
    # (Memories are stored separately per entity, so scope one dimension at a time.)
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"run_id": "*"},
            {"app_id": "*"}
        ]
    }
    ```
  </Accordion>
</AccordionGroup>

## Advanced examples

Level up foundational patterns with compound filters that coordinate entity scope, tighten time windows, and weave in exclusion rules for high-precision retrievals.

<AccordionGroup>
  <Accordion title="Multi-dimensional filtering">
    ```python
    # Invoice memories in Q1 2024
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"keywords": {"icontains": "invoice"}},
            {"categories": {"in": ["finance"]}},
            {"created_at": {"gte": "2024-01-01T00:00:00Z"}},
            {"created_at": {"lt": "2024-04-01T00:00:00Z"}}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Entity-specific retrieval">
    ```python
    # Query agent scope on its own
    filters = {
        "AND": [
            {"agent_id": "finance_bot"}
        ]
    }

    # Or broaden within that scope using wildcards
    filters = {
        "AND": [
            {"agent_id": "finance_bot"},
            {"run_id": "*"}
        ]
    }
    ```
  </Accordion>

  <Accordion title="Nested NOT/OR logic">
    ```python
    # User memories from 2024, excluding spam and test
    filters = {
        "AND": [
            {"user_id": "user_123"},
            {"created_at": {"gte": "2024-01-01T00:00:00Z"}},
            {"NOT": {
                "OR": [
                    {"categories": {"in": ["spam"]}},
                    {"categories": {"in": ["test"]}}
                ]
            }}
        ]
    }
    ```
  </Accordion>
</AccordionGroup>

## Best practices

<Callout type="tip" icon="lightbulb" color="#26A17B">
The root must be `AND`, `OR`, or `NOT` with an array of conditions.
</Callout>

<Callout type="tip" icon="lightbulb" color="#26A17B">
Use `"*"` to match any non-null value for a field.
</Callout>

<Callout type="warning" icon="exclamation-triangle" color="#E74C3C">
Memories are stored per-entity (user, agent, app, run). Combining `user_id` **and** `agent_id` in the same `AND` clause returns no results because no record contains both values at once. Query one entity scope at a time or use `OR` logic for parallel lookups.
</Callout>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Missing results with agent_id">
    **Problem**: Filtered by `user_id` but don't see agent memories.

    **Solution**: User and agent memories are stored as separate records. Use OR to query both scopes:
    ```python
    {"OR": [{"user_id": "user_123"}, {"agent_id": "agent_name"}]}
    ```
  </Accordion>

  <Accordion title="ne operator returns too much">
    **Problem**: `ne` comparison pulls in records with null values.

    **Solution**: Pair `ne` with a wildcard guard:
    ```python
    {"AND": [{"agent_id": "*"}, {"agent_id": {"ne": "old_agent"}}]}
    ```
  </Accordion>

  <Accordion title="Case-insensitive search">
    **Solution**: Swap to `icontains` to normalize casing.
  </Accordion>

  <Accordion title="Date range between two dates">
    **Solution**: Use `gte` for the start and `lt` for the end boundary:
    ```python
    {"AND": [
        {"created_at": {"gte": "2024-01-01"}},
        {"created_at": {"lt": "2024-02-01"}}
    ]}
    ```
  </Accordion>

  <Accordion title="Metadata filter not working">
  **Solution**: Match top-level metadata keys exactly:
  ```python
  {"metadata": {"source": "email"}}
  ```
</Accordion>
</AccordionGroup>

## FAQ

<AccordionGroup>
  <Accordion title="Do I need AND/OR/NOT?">
    Yes. The root must be a logical operator with an array.
  </Accordion>

  <Accordion title="What does * match?">
    Any non-null value. Nulls are excluded.
  </Accordion>

  <Accordion title="Why use wildcards?">
    Unspecified fields default to NULL. Use `"*"` to include non-null values.
  </Accordion>

  <Accordion title="Is = required?">
    No. Equality is the default: `{"user_id": "u1"}` works.
  </Accordion>

  <Accordion title="Can I filter nested metadata?">
    Only top-level keys are supported.
  </Accordion>

  <Accordion title="How to search text?">
    Use `keywords` with `contains` (case-sensitive) or `icontains` (case-insensitive).
  </Accordion>

<Accordion title="Can I nest AND/OR?">
    ```python
    {
        "AND": [
            {"user_id": "user_123"},
            {"OR": [
                {"categories": "finance"},
                {"categories": "health"}
            ]}
        ]
    }
    ```
  </Accordion>
</AccordionGroup>

## Known limitations

- Entity filters operate on a single scope per record. Use separate queries or `OR` logic to compare users vs agents.
- Metadata supports only bare/`eq`, `contains`, and `ne` comparisons.
- Wildcards (`"*"` ) match only records where the field is already non-null.
