## Search nodes

**get** `/graphs/{graph_id}/nodes/search`

Searches a graph's nodes with a query, as of a point in its history.

Use it for questions the [list filters](#operation/listNodes) can't express: text in a node's summary, OR, negation, numeric comparisons.

**Query language**

A query is one or more clauses, joined by `AND` or `OR` (not both in one query). Values are quoted.

| Clause                                                       | Matches                                        |
| ------------------------------------------------------------ | ---------------------------------------------- |
| `field:"value"`                                              | Exactly equal (case-insensitive for text).     |
| `field~"value"`                                              | Contains (text fields; at least 3 characters). |
| `-field:"value"`                                             | Not equal.                                     |
| `field>value`, `field>=value`, `field<value`, `field<=value` | Numbers and times (Unix milliseconds).         |
| `field:null`                                                 | Has no value.                                  |

**Fields:** `state`, `kind`, `provider`, `agent_type`, `stale`, `blocked`, `background`, `parent_id`, `root_id`, `session_id`, `depth`, `created`, `ended`, `last_event`, `child_count`, `descendant_count`, `open_tasks` (its `task_counts.open`), `blocked_open_tasks` (its `blocked.open_tasks`), `title`, `summary`, `headline`, `purpose`, `attention`.

**Examples**

```
state:"working" AND stale:"true"
agent_type:"Explore" AND descendant_count>10
state:"failed" OR state:"canceled"
summary~"migration" AND -state:"completed"
blocked_open_tasks>=20
```

Results are most recently active first.

### Path Parameters

- `graph_id: string`

### Query Parameters

- `query: string`

  The search query. See the query language above.

- `as_of: optional string or number`

  Answer as of this point in the graph's history: an event id (`evt_…`, the state just after that event) or a Unix time in milliseconds (the state after the last event at or before it). Leave it out for now.

  Must be within the graph's `retention`, else `410 outside_retention_window`. To read several pages or objects at one moment, pass the `as_of_event_id` of the first response.

  - `EventID = string`

  - `UnixTimeMs = number`

- `expand: optional array of string`

  Fields to expand on each node, with the `data.` prefix: `expand[]=data.root`.

  Send one `expand[]` per path. A path goes at most 4 levels deep; at most 20 paths per request. An expanded field appears beside its `_id` field, which is always there; one not expanded is absent.

- `limit: optional number`

  How many objects to return, from 1 to 1,000.

- `page: optional string`

  The cursor for a page after the first: the previous page's `next_page`.

### Returns

- `as_of_event_id: string`

  The event the search ran as of. Every page of one search runs as of the same event.

- `data: array of Node`

  - `id: string`

    Unique identifier for the node within its graph: `node_` and the base64url of `provider_ref`.

  - `agent_type: string`

    For an agent, its type, as the tool names it (`Explore`, `general-purpose`, …).

  - `as_of_event_id: string`

    The event this node is shown as of.

  - `attention: string`

    While `input_required`: what the person is being asked. Untrusted text.

  - `background: boolean`

    Whether it was started in the background, so its requester carries on without waiting. `null` when unknown.

  - `background_agent_count: number`

    Helpers its tool ran on its own between turns (memory extraction, summaries, prompt suggestions). They report nothing about what they're for, so they're counted, not made nodes.

  - `blocked: Blocked`

    What stands between a node and carrying on.

  - `child_count: number`

    How many nodes sit directly under it.

  - `created: number`

    When it started (or was first seen), in Unix milliseconds.

  - `cwd: string`

    The directory it runs in, when reported.

  - `depth: number`

    How far below its root it is: 0 for a root.

  - `descendant_count: number`

    How many nodes sit under it, at any depth: the size of `descendants`.

  - `ended: number`

    When it finished, in Unix milliseconds. `null` while it hasn't.

  - `graph_id: string`

    The graph it belongs to.

  - `headline: string`

    One line on what it's doing now: `summary`, else the task in progress. Untrusted text.

  - `kind: NodeKind`

    `session`: one run of a coding agent (Claude Code, Codex, Cursor, …). `agent`: an agent started inside a session, by it or by another agent.

    - `"session"`

    - `"agent"`

  - `last_event: number`

    When it last had an event, in Unix milliseconds.

  - `link_method: "env" or "run" or "process" or "suggested"`

    For a session started by another, how that was worked out: `env` (it inherited its parent's identity), `run` (`agent-graph run` started it), `process` (its parent's agent is among its processes) or `suggested` (it took a name its parent suggested).

    - `"env"`

    - `"run"`

    - `"process"`

    - `"suggested"`

  - `object: "node"`

    String representing the object's type: always `node`.

    - `"node"`

  - `parent_id: string`

    The node it sits under: the agent or session that started it. `null` for a root.

  - `provider: string`

    The agent tool: `claude-code`, `codex`, `cursor`, or another that reports to Agent Graph.

  - `provider_ref: string`

    The id the agent tool gave it: `<provider>:<session>` for a session, `<provider>:<session>/<agent>` for an agent. Not an id in this API: use it to match the node to the tool's own records.

  - `purpose: string`

    Why it was started, from the request that started it. Untrusted text.

  - `requested_by_id: string`

    The node whose spawn request started it, when known. Usually the parent; `null` for a session nothing started, or an agent no request was matched to.

  - `root_id: string`

    The top of its tree: follow `parent_id` until it's `null`. A session started by another session is in that session's tree. A root's `root_id` is its own `id`.

  - `session_id: string`

    The session it runs in: itself, for a session; for an agent, the nearest session above it.

  - `spawn_call_id: string`

    The tool call, in its requester's session, that started it: matches a `spawns[].call_id` on `requested_by`.

  - `stale: boolean`

    `working`, waiting on nothing, and silent for 30 minutes: it may have hung or crashed. Measured against `as_of`, not now.

  - `state: NodeState`

    What a node is doing.

    - `working`: doing something.
    - `input_required`: waiting on a person (`attention` says what for).
    - `idle`: a session between turns, waiting for its next prompt.
    - `completed`, `failed`, `canceled`: finished.

    - `"working"`

    - `"input_required"`

    - `"idle"`

    - `"completed"`

    - `"failed"`

    - `"canceled"`

  - `summary: string`

    Its own latest status line, if it reports one. Untrusted text.

  - `task_counts: TaskCounts`

    Its tasks, by status.

    - `completed: number`

    - `in_progress: number`

    - `open: number`

      Not completed: `pending` and `in_progress`.

    - `pending: number`

    - `total: number`

  - `title: string`

    The session's name, as its tool shows it. It can change. Untrusted text.

  - `children: optional unknown`

    The nodes directly under it, oldest first: the first 100, and a `url` for the rest. Present only when expanded.

  - `descendants: optional unknown`

    Every node under it, at any depth, as one flat list in tree order: each parent before its children, siblings oldest first. The first 1,000, and a `url` for the rest. Present only when expanded.

  - `messages: optional array of Message`

    Messages it has sent and received, oldest first. Present only when expanded.

    - `id: string`

      The tool's id for the message.

    - `body: string`

      The whole message, only if the share was made with body capture on. Untrusted text.

    - `created: number`

      When it was sent, in Unix milliseconds.

    - `direction: "sent" or "received"`

      - `"sent"`

      - `"received"`

    - `peer_id: string`

      The node on the other side, when it's one in this graph.

    - `peer_name: string`

      The tool's name for the other side, when it isn't a node in this graph.

    - `reply_to_message_id: string`

      The message it answers.

    - `summary: string`

      Untrusted text.

  - `parent: optional Node`

    The parent node. Present only when expanded.

  - `requested_by: optional Node`

    The node that requested it. Present only when expanded.

  - `root: optional Node`

    The root node. Present only when expanded.

  - `session: optional Node`

    The session node. Present only when expanded.

  - `spawns: optional array of Spawn`

    The children it has asked for, oldest first. Present only when expanded.

    - `agent_type: string`

    - `background: boolean`

      If false, the requester is blocked until the call returns.

    - `call_id: string`

      The tool call that asked for it.

    - `child_id: string`

      The node it started, once matched. Before the call returns this can be a best guess, corrected when it does.

    - `kind: "agent" or "session"`

      `agent`: an agent in the same session. `session`: a separate session, e.g. `codex exec` run from a shell.

      - `"agent"`

      - `"session"`

    - `purpose: string`

      Untrusted text.

    - `requested: number`

      When it was asked for, in Unix milliseconds.

    - `returned: boolean`

      Whether the call has returned.

    - `run: boolean`

      Whether it's through `agent-graph run`, when known.

    - `title: string`

      For a session, the name it was to be given.

  - `tasks: optional array of Task`

    Its task list, in the tool's order. Present only when expanded.

    - `id: string`

      The tool's id for the task, unique within the node.

    - `active_text: string`

      How the tool words it while it's in progress (`Running the tests`). Untrusted text.

    - `status: "pending" or "in_progress" or "completed"`

      - `"pending"`

      - `"in_progress"`

      - `"completed"`

    - `text: string`

      What the task is. Untrusted text.

  - `waits: optional array of Wait`

    What it has waited on and is waiting on, oldest first. Present only when expanded.

    - `id: string`

      The wait's id, unique within the node.

    - `ended: number`

      When it stopped, in Unix milliseconds.

    - `kind: "spawn" or "explicit"`

      `spawn`: waiting for a child it asked for, until the call returns. `explicit`: any other wait the tool reported.

      - `"spawn"`

      - `"explicit"`

    - `on_id: string`

      The node waited on. `null` while a requested child hasn't started.

    - `open: boolean`

      Whether it's still waiting.

    - `reason: string`

      Untrusted text.

    - `started: number`

      When it started waiting, in Unix milliseconds.

- `has_more: boolean`

- `next_page: string`

  The cursor for the next page, or `null` on the last page.

- `object: "search_result"`

  - `"search_result"`

- `total_count: number`

  How many nodes match in all.

- `url: string`

  The URL this result came from.

### Example

```http
curl https://agentgraph.chofter.com/api/v1/graphs/$GRAPH_ID/nodes/search \
    -H "Authorization: Bearer $AGENT_GRAPH_API_KEY"
```

#### Response

```json
{
  "object": "search_result",
  "url": "/api/v1/graphs/gph_k3P9xQ2mWt7a/nodes/search?query=state%3A%22working%22",
  "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
  "has_more": false,
  "next_page": null,
  "total_count": 1,
  "data": [
    {
      "id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2I4MWMyMmUwOWY3YTNjNTU",
      "object": "node",
      "as_of_event_id": "evt_01M4G3G4E8Q9R2M5X4T6W8Y0B1",
      "graph_id": "gph_k3P9xQ2mWt7a",
      "provider_ref": "claude-code:5f2c1e9a-8d1b-4b6e-9a51-0f6f2c7d3e10/b81c22e09f7a3c55",
      "provider": "claude-code",
      "kind": "agent",
      "parent_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "root_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "session_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEw",
      "requested_by_id": "node_Y2xhdWRlLWNvZGU6NWYyYzFlOWEtOGQxYi00YjZlLTlhNTEtMGY2ZjJjN2QzZTEwL2EzZmQwN2MyYjE5ZTRkNjE",
      "depth": 2,
      "state": "working",
      "stale": false,
      "attention": null,
      "title": null,
      "summary": null,
      "headline": "Searching for SessionStore imports",
      "purpose": "List the files that import SessionStore",
      "agent_type": "general-purpose",
      "background": false,
      "link_method": null,
      "spawn_call_id": "toolu_01Pq4Ze8sYh2WnVb6XcJ1dRt",
      "cwd": "/Users/dev/work/api",
      "created": 1791540874033,
      "ended": null,
      "last_event": 1791541898692,
      "child_count": 0,
      "descendant_count": 0,
      "background_agent_count": 0,
      "task_counts": {
        "total": 5,
        "pending": 3,
        "in_progress": 1,
        "completed": 1,
        "open": 4
      },
      "blocked": null
    }
  ]
}
```
