# Search AgentOS sessions with nested filters

> Agno AgentOS adds POST /sessions/search and GET /sessions/filter-schema. A search combines AND, OR and NOT conditions on session names, IDs, type and timestamps, and returns one page of matches with the total count.

- Published: 2026-10-27
- Author: Priti S
- Categories: Changelog
- Canonical: https://www.agno.com/blog/search-agentos-sessions-with-nested-filters
- Markdown: https://www.agno.com/blog/search-agentos-sessions-with-nested-filters.md

Agno AgentOS answers `POST /sessions/search` with one page of matching sessions and a `meta.total_count` that covers every match. The request body carries a `filter` that nests `AND`, `OR` and `NOT` over nine fields: `session_id`, `session_name`, `user_id`, `agent_id`, `team_id`, `workflow_id`, `session_type`, `created_at` and `updated_at`.

`GET /sessions/filter-schema` lists those fields with their type and allowed operators, so a client can build its filter editor from the response. ID and name fields take `EQ`, `NEQ`, `IN`, `CONTAINS` and `STARTSWITH`, and the last two ignore case. Timestamps take comparisons against an ISO date or epoch seconds. The expression format is the one [trace search](https://docs.agno.com/agent-os/tracing/filter-options) already uses.

The Python client sends the same search with `search_sessions`, or `asearch_sessions` in async code:

```python
from agno.client import AgentOSClient

client = AgentOSClient(base_url="http://localhost:7777")
result = client.search_sessions(
    filter_expr={
        "op": "AND",
        "conditions": [
            {
                "op": "OR",
                "conditions": [
                    {"op": "CONTAINS", "key": "session_name", "value": "billing"},
                    {"op": "STARTSWITH", "key": "session_name", "value": "refund"},
                ],
            },
            {"op": "NOT", "condition": {"op": "EQ", "key": "user_id", "value": "bob"}},
            {"op": "GTE", "key": "created_at", "value": "2026-01-01T00:00:00Z"},
        ],
    },
    limit=2,
    sort_order="asc",
)
print(result.meta.total_count, result.meta.total_pages, [s.session_name for s in result.data])
# 3 2 ['Billing question', 'Refund request']
```

We seeded the six sample sessions from the cookbook (two agents, a team, two users) and ran that filter through AgentOS with FastAPI's `TestClient`. SQLite, PostgreSQL and the JSON file database returned the same three sessions over two pages.

![Real output of POST /sessions/search with limit 2. The filter is AND of three parts: an OR of session_name contains billing or starts with refund, NOT user_id equals bob, and created_at on or after 1 January 2026. Page 1 returns Billing question and Refund request, page 2 returns Billing escalation, all for alice. total_count is 3 and total_pages is 2. Bob's billing dispute is left out.](https://www.agno.com/images/v3-1-11-session-search.png)

AgentOS checks the whole expression before it queries the database and answers `400` for anything it can't run:

```text
{"op": "LIKE", "key": "user_id", "value": "x"}
400 Invalid session search: Unsupported operator for user_id: LIKE

{"op": "EQ", "key": "title", "value": "x"}
400 Invalid session search: Unknown session filter field: title
```

PostgreSQL, SQLite, MySQL, SingleStore and MongoDB apply the filter inside the query, before counting and paging. Other adapters read every session the caller can see in one pass, filter in memory, then load runs only for the unnamed sessions on the returned page. A session with no value for a field never matches a condition on that field, even under `NOT`. `session_name` matches saved names only, so a session listed under a title taken from its first message won't match a name filter.

The route needs the `sessions:read` scope and keeps the caller's user scoping. A page holds at most 100 sessions.

See the [cookbook](https://github.com/agno-agi/agno/tree/main/cookbook/05_agent_os/31_session_filters), and learn more about the [AgentOS client](https://docs.agno.com/agent-os/client/agentos-client) in the documentation.
