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 already uses.
The Python client sends the same search with search_sessions, or asearch_sessions in async code:
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.

AgentOS checks the whole expression before it queries the database and answers 400 for anything it can't run:
{"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: titlePostgreSQL, 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, and learn more about the AgentOS client in the documentation.
Frequently asked questions
Send POST /sessions/search with a filter such as {"op": "AND", "conditions": [{"op": "EQ", "key": "user_id", "value": "alice"}, {"op": "EQ", "key": "agent_id", "value": "support"}]}. The response holds one page of sessions in data and the total match count in meta.total_count.
GET /sessions/filter-schema returns the list. ID and name fields take EQ, NEQ, IN, CONTAINS and STARTSWITH, session_type takes EQ, NEQ and IN, and created_at and updated_at take EQ, NEQ, GT, GTE, LT and LTE with an ISO date or epoch seconds. AND, OR and NOT combine them.
Yes. PostgreSQL, SQLite, MySQL, SingleStore and MongoDB filter inside the database query. Every other Agno database reads the sessions the caller can see once and filters them in memory, so the cost grows with the number of sessions.
AgentOSClient has search_sessions and an async asearch_sessions. Both take filter_expr, page, limit, sort_by and sort_order, and a user_id argument adds an equality condition on that user to the filter.


