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

# Get session changes

> Long-poll for real-time session updates.

export const ChangesLoop = () => {
  const stroke = {
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.75,
    strokeLinecap: "round",
    strokeLinejoin: "round"
  };
  const S = c => ({
    className: c,
    ...stroke
  });
  const icons = {
    stream: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="M4 6h16M4 12h16M4 18h10" /></svg>,
    pulse: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="M22 12h-4l-3 9L9 3l-3 9H2" /></svg>
  };
  const Card = ({icon, title, sub, children}) => <div className="flex shrink-0 flex-col self-center rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
      <div className="flex items-center gap-2.5">
        <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">{icon("h-5 w-5")}</span>
        <div>
          <div className="whitespace-nowrap font-mono text-[15px] font-semibold leading-6 text-zinc-900 dark:text-zinc-100">{title}</div>
          <div className="whitespace-nowrap text-sm text-zinc-500 dark:text-zinc-400">{sub}</div>
        </div>
      </div>
      {children}
    </div>;
  const Arrow = ({top}) => <div className="flex min-w-[90px] flex-1 flex-col items-stretch justify-center gap-1 px-3 text-center text-xs leading-4 text-zinc-500 dark:text-zinc-400">
      <span className="whitespace-nowrap">{top}</span>
      <div className="flex items-center text-zinc-400 dark:text-zinc-600">
        <span className="h-px flex-1 bg-current" />
        <svg className="-ml-px h-3 w-2 shrink-0" viewBox="0 0 8 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1 1.5 6 6l-5 4.5" /></svg>
      </div>
    </div>;
  const Chip = ({children}) => <span className="whitespace-nowrap rounded-md bg-zinc-100 px-2 py-0.5 font-mono text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">{children}</span>;
  const Line = ({chip, children}) => <div className="flex items-center gap-2 whitespace-nowrap"><Chip>{chip}</Chip>{children}</div>;
  return <div className="not-prose my-8 overflow-x-auto">
      <div className="flex min-w-[560px] flex-col pb-2">
        <div className="flex items-stretch">
          <Card icon={icons.stream} title="GET /changes" sub="long poll, up to 25s">
            <div className="mt-4 flex flex-col items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400">
              <Line chip="200">new events, advance <span className="font-mono text-[13px]">from_index</span></Line>
              <Line chip="204">nothing new yet</Line>
            </div>
          </Card>

          <Arrow top="then check" />

          <Card icon={icons.pulse} title="GET /status" sub="the only source of truth">
            <div className="mt-4 flex flex-col items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400">
              <Line chip="settled">drain, read the answer</Line>
              <Line chip="running">keep polling</Line>
            </div>
          </Card>
        </div>

        <div className="relative mx-20 h-7 rounded-b-xl border-b border-l border-r border-zinc-300 dark:border-zinc-700">
          <svg className="absolute -left-[6.5px] -top-1 h-2 w-3 text-zinc-400 dark:text-zinc-600" viewBox="0 0 12 8" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1.5 7 6 2l4.5 5" /></svg>
          <div className="absolute -bottom-2 left-0 right-0 flex justify-center"><span className="whitespace-nowrap bg-white px-2 text-xs leading-4 text-zinc-500 dark:bg-zinc-950 dark:text-zinc-400">back to /changes</span></div>
        </div>
      </div>
    </div>;
};

Returns a stream of changes (events, status transitions, agent actions) that have occurred since your last request. Uses **long polling**: the server holds the connection open until new changes are available or the timeout expires.

**Returns** `200` with a `SessionChanges` object, or `204 No Content` if no new events arrive within the wait period.

`changes` is a delta: each call returns only what's new since `from_index`, and `204` when nothing new has arrived yet. Note that `status` can read `completed` while events (including the [`answer`](#response)) are still unread on later pages, so keep advancing `from_index` until the session is [terminal](/agents-api/sessions/overview#lifecycle) *and* a poll returns no further events. To skip the loop entirely and just read a finished run's result, use [`latest_answer`](/agents-api/sessions/retrieve); the [SDK helper](#long-polling-pattern) drains for you.

***

## Path parameters

<ParamField path="id" type="string" required>
  The session ID.
</ParamField>

***

## Query parameters

<ParamField query="from_index" type="integer" default="0">
  Event index to start from. Use this to resume from where you left off.
</ParamField>

<ParamField query="limit" type="integer">
  Maximum number of events to return.
</ParamField>

<ParamField query="include_events" type="boolean" default="true">
  Whether to include event details in the response.
</ParamField>

<ParamField query="wait_for_seconds" type="integer" default="0">
  How long the server should hold the connection waiting for changes, up to `25` seconds. The default `0` returns immediately. Set `20` to `25` for efficient long polling.
</ParamField>

***

## Response

```json Response theme={"system"}
{
  "status": "running",
  "started_at": "2026-05-07T14:30:02Z",
  "finished_at": null,
  "error": null,
  "error_code": null,
  "answer": null,
  "outcome": null,
  "metrics": {
    "steps": 2,
    "total_cost": 0.0093874,
    "input_cost": 0.0084664,
    "output_cost": 0.000921,
    "reasoning_cost": 0.0,
    "cost_per_model": [
      {
        "name": "holo3-122b-a10b",
        "input_tokens": 21166,
        "output_tokens": 307,
        "reasoning_tokens": 0,
        "input_cost": 0.0084664,
        "output_cost": 0.000921,
        "reasoning_cost": 0.0,
        "total_cost": 0.0093874
      }
    ]
  },
  "new_events": [
    { "type": "AgentEvent", "data": { "...": "..." }, "timestamp": "2026-05-07T14:30:05Z" }
  ]
}
```

| Field         | Type                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`      | string                   | Current session status.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `started_at`  | string \| null           | ISO 8601 timestamp when the agent started executing.                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `finished_at` | string \| null           | ISO 8601 timestamp when the session reached a terminal state.                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `error`       | string \| null           | Short, stable error message if the session failed or timed out. Branch on `error_code`, not on this text.                                                                                                                                                                                                                                                                                                                                                                                 |
| `error_code`  | string \| null           | Machine-readable failure category when the session failed or timed out: `environment_error`, `no_answer`, `answer_validation`, `timeout`, or `internal`. See [Read how the run ended](/agents-api/observe-and-steer#read-how-the-run-ended).                                                                                                                                                                                                                                              |
| `outcome`     | string \| null           | The agent's self-assessed task outcome, reported with its final answer: `success`, `partial`, `infeasible`, or `blocked`. See [Read how the run ended](/agents-api/observe-and-steer#outcomes).                                                                                                                                                                                                                                                                                           |
| `answer`      | string \| object \| null | The agent's final result once produced; `null` otherwise. A string by default, or an object matching the agent's [`answer_format`](/agents-api/agents/overview) JSON Schema when one was set. It rides the page that delivers the final events, so keep polling until the session is [terminal](/agents-api/sessions/overview#lifecycle) and drained. For a cursor-independent read, the same value is mirrored on the [Session object](/agents-api/sessions/overview)'s `latest_answer`. |
| `metrics`     | object                   | Usage and cost rolled up to the moment of the response: `steps`, `total_cost`, `input_cost`, `output_cost`, `reasoning_cost`, and `cost_per_model[]` (each entry carries per-model tokens and costs, including `reasoning_tokens`). Cost fields are in USD and are `null` when a model's price is unavailable, so null-check before summing.                                                                                                                                              |
| `new_events`  | array                    | Events since `from_index`, each following the [event shape](/agents-api/sessions/events#event-shape) (`type`, `data`, `timestamp`).                                                                                                                                                                                                                                                                                                                                                       |

***

## Examples

<CodeGroup>
  ```bash cURL theme={"system"}
  curl "https://agp.eu.hcompany.ai/api/v2/sessions/$SESSION_ID/changes?from_index=0&wait_for_seconds=25" \
    -H "Authorization: Bearer $HAI_API_KEY"
  ```

  ```python Python theme={"system"}
  from hai_agents import Client

  client = Client()

  changes = client.sessions.get_session_changes(
      session_id,
      from_index=0,
      wait_for_seconds=25,
  )
  ```

  ```typescript TypeScript theme={"system"}
  import { HaiAgentsClient } from "hai-agents";

  const client = new HaiAgentsClient();

  const changes = await client.sessions.getSessionChanges({
    id: sessionId,
    fromIndex: 0,
    waitForSeconds: 25,
  });
  ```
</CodeGroup>

***

## Long-polling pattern

Long polling is more efficient than repeated status checks because the server only responds when something actually changes. The SDK ships a helper that runs the loop for you: it drives termination off `status` (authoritative) while streaming events from `changes`, resuming `from_index` and handling the `204` no-change responses automatically.

<ChangesLoop />

<CodeGroup>
  ```python Python theme={"system"}
  from hai_agents import wait_for_session

  result = wait_for_session(client, session_id, wait_for_seconds=25)

  for event in result.events:
      print(event.type)
  print(result.status, result.answer)

  # client.run_session(...) creates the session and runs this loop in one call.
  ```

  ```typescript TypeScript theme={"system"}
  import { waitForSession } from "hai-agents";

  const result = await waitForSession(client, { id: sessionId, waitForSeconds: 25 });

  for (const event of result.events) {
    console.log(event.type);
  }
  console.log(result.status, result.answer);

  // client.runSession(...) creates the session and runs this loop in one call.
  ```
</CodeGroup>

Long-poll `changes` to follow a run: it returns new events and the final `answer` with near-instant latency and far fewer calls than fixed-interval polling. Reach for [`status`](/agents-api/sessions/status) only when you want a cheap, one-off liveness check.
