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

# Python SDK: live logs and status

> Stream a Square Cloud app's live logs, status and system events with client.apps.realtime() in the Python SDK, sync or async, with automatic reconnection.

`client.apps.realtime(app_id)` opens a live stream of an app's logs, status and system events. With `SquareCloud` it is an **iterator** consumed with `for`; with `AsyncSquareCloud` it is an **async iterator** consumed with `async for`. Use it in a `with` / `async with` block so leaving the block closes the connection.

Examples use the `client` from [Creating the client](/en/sdks/py/client#creating-the-client). `app_id` is the id of one of your apps: [`client.account.me()`](/en/sdks/py/client#account) lists them.

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    with client.apps.realtime(app_id) as stream:
        for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    async with client.apps.realtime(app_id) as stream:  # no await: realtime() is not a coroutine
        async for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>
</Tabs>

With `AsyncSquareCloud`, `apps.realtime(app_id)` returns an `AsyncRealtime` directly: do **not** `await` it. A background reader thread feeds the events to your event loop, so no executor thread is held while you wait.

## Events

Every event is a dict with `event`, `data` (the raw frame text) and `id` (`None` when the frame has none).

| `event` | Extra fields | Description |
| - | - | - |
| `logs` | `stream`, `line` | One log line. The first character of `data` tells the stream (`\u0001` stdout, `\u0002` stderr); the SDK strips it into `line` and sets `stream` to `"stdout"` or `"stderr"` |
| `status` | `status` | Live container metrics. The first frame is complete and the next ones only carry what changed: the SDK merges them, so `status` is always the full state |
| `system` | | Connection events such as `REALTIME_CONNECTING \| <id>`, `REALTIME_TIMEOUT` or `REALTIME_DISCONNECTED` |
| `error` | | An error code such as `CONTAINER_NOT_FOUND` |
| `message` | | A frame without an `event` name |

### The `status` object

| Field | Description |
| - | - |
| `cpu` | CPU usage (%) |
| `cpuLimit` | Number of CPU cores allocated |
| `ram` | `[usedMB, limitMB]` |
| `status` | Container state, e.g. `running` |
| `netIO` | Network `{ i, o }` totals, with `new` for the latest interval |
| `bIO` | Disk `{ i, o }` totals |
| `uptime` | Unix ms of the container start |

## Ending the stream

The loop ends on its own when:

* the server closes the connection cleanly (each connection lasts up to **10 minutes**);
* the server sends `REALTIME_DISCONNECTED`;
* you `break` out of the loop (then leave the `with` block, which closes the connection);
* you call `stream.close()`, from any thread, even while the loop waits for data or for a reconnection (the loop just ends, it does not raise).

`close()` is synchronous on both `Realtime` and `AsyncRealtime`. To keep watching past 10 minutes, open a new stream when the loop ends.

```python theme={"system"}
import threading

stream = client.apps.realtime(app_id)
threading.Timer(60, stream.close).start()  # stop after one minute

for event in stream:
    print(event["event"], event["data"])
```

## Reconnection

A dropped connection, or the server handing reconnection to the client (`REALTIME_RECONNECT`), reopens the stream on its own:

* up to **3 times in a row**; any `logs` or `status` event resets the count;
* each reopen at least **5.5 s** after the previous open, to stay under the API's pace of one open per 5 seconds per app.

Past 3 failed reconnections, the loop raises a `SquareCloudAPIError` with `NETWORK_ERROR`. Each open is a `GET`, so a network error while opening is also [retried](/en/sdks/py/errors#retries) up to `max_retries` times; an open that still fails raises.

## Limits and errors

* **5** concurrent connections per account and **30** per app, across all users. Going over is 429 `REALTIME_MAX_CONNECTIONS` or `REALTIME_MAX_CONNECTIONS_APP`.
* The HTTP status is checked before streaming, so these errors (and 404 for an unknown app) raise on the first iteration.
* The timeout only covers the open, until the response headers arrive. The stream itself has no timeout: a half-open connection (a laptop resuming from sleep) waits until you call `close()`.

## Next steps

<CardGroup cols={3}>
  <Card title="Snapshots" icon="clock-rotate-left" href="/en/sdks/py/snapshots">
    Back up and restore apps and databases.
  </Card>

  <Card title="Realtime API reference" icon="code" href="/en/api-reference/endpoint/apps/realtime">
    The REST endpoint behind this stream.
  </Card>

  <Card title="Logs from the CLI" icon="terminal" href="/en/cli-reference/logs">
    The same stream from the terminal.
  </Card>
</CardGroup>
