> Full docs index: https://docs.steel.dev/llms.txt

# Session Lifecycle
URL: https://docs.steel.dev/overview/sessions-api/session-lifecycle

### Overview
Sessions are the foundation of browser automation in Steel. Each session represents an isolated browser instance that persists until it's either explicitly released or times out.

Each session can be in one of three states:

*   **Live**: The session is active and ready to accept commands/connections. This is the state right after creation and during normal operation.

*   **Released**: The session has been intentionally shut down, either through explicit release or timeout. Resources have been cleaned up. Can no longer accept commands/connections.

*   **Failed**: Something went wrong during the session's lifetime (like a crash or connection loss). These sessions are automatically cleaned up.

Browser sessions are billed by the minute, rounded up. Launch sessions can last up to 15 minutes, Scale sessions up to 1 hour, and Enterprise sessions up to 24 hours. See [Pricing/Limits](/overview/pricinglimits) for current rates and limits.

Understanding how sessions live and die helps you manage resources effectively and build more reliable applications.

### Reserving a Session ID

Steel generates a session ID for you, but `create` also accepts one. Pass your own UUID when the ID has to exist before the browser does: a job row you write before starting the session, or a live view link you hand to a user while the browser is still booting.

```typescript
import { randomUUID } from 'node:crypto';
import Steel from 'steel-sdk';

const client = new Steel();

// Reserve the ID first, then create the session with it.
const sessionId = randomUUID();
await jobs.insert({ sessionId, status: 'starting' });

const session = await client.sessions.create({ sessionId });
console.log(session.id === sessionId); // true
```

```python
import uuid
from steel import Steel

client = Steel()

# Reserve the ID first, then create the session with it.
session_id = str(uuid.uuid4())
jobs.insert(session_id=session_id, status="starting")

session = client.sessions.create(session_id=session_id)
print(session.id == session_id)  # True
```

The value must be a valid UUID such as `123e4567-e89b-12d3-a456-426614174000`. Omit `sessionId` and Steel generates one instead. Either way, the ID goes to the same places:

*   `client.sessions.release(sessionId)` to end the session

*   `client.sessions.files.list(sessionId)` and the rest of the Files API

*   `wss://connect.steel.dev?apiKey=<key>&sessionId=<id>` to attach Playwright or Puppeteer

### Session Lifetime and Timeout

When you start a session, it stays alive for 5 minutes by default but you can change it by passing the timeout parameter, up to your plan's maximum session time. After the time passes, the session will be automatically released.

```typescript
import Steel from 'steel-sdk';

const client = new Steel();

// Create session and keep it running for 10 minutes.
const session = await client.sessions.create({
  timeout: 600000 // 10 minutes (NOTE: Units are in milliseconds)
});
```

```python
import os
from steel import Steel

client = Steel()

# Create session and keep it running for 10 minutes.
session = client.sessions.create(
    api_timeout=600000 # 10 minutes (NOTE: Units are in milliseconds)
)
```

**Note:** Currently, Steel doesn’t support editing the timeout duration of a live session.

### Inactivity Timeout

By default a session runs until its `timeout` elapses, even when nothing is driving the browser. Set `inactivityTimeout` to release the session early once it stops seeing activity—any CDP command, Commands API request, or remote input—so you don’t keep paying for an idle browser while waiting on an external step.

```typescript
import Steel from 'steel-sdk';

const client = new Steel();

// Release the session after 1 minute with no CDP or input activity,
// capped at a 10-minute hard limit.
const session = await client.sessions.create({
  timeout: 600000,          // 10 minutes (hard cap)
  inactivityTimeout: 60000, // release after 1 minute of inactivity
});
```

```python
import os
from steel import Steel

client = Steel()

# Release the session after 1 minute with no CDP or input activity,
# capped at a 10-minute hard limit.
session = client.sessions.create(
    api_timeout=600000,       # 10 minutes (hard cap)
    inactivity_timeout=60000, # release after 1 minute of inactivity
)
```

**Note:** `timeout` is always the hard cap on a session’s lifetime. If `inactivityTimeout` is greater than or equal to `timeout` it has no effect—`timeout` elapses first. Omit `inactivityTimeout` to disable inactivity-based release (the default).

### **Releasing a Session**

When you're done with a session, it's best practice to release it explicitly rather than waiting for the timeout. You can release a session any time before the timeout is up by calling the `release` method.

```typescript
// Release a single session
const response = await client.sessions.release(session.id);
```

```python
# Release a single session
response = client.sessions.release(session.id)
```

#### Bulk Session Release

Sometimes you need to clean up all active sessions at once. Steel provides a convenient way to do this:

```typescript
// Release all active sessions
const response = await client.sessions.releaseAll();
console.log(response.message); // "All sessions released successfully"
```

```python
# Release all active sessions
response = client.sessions.release_all()
print(response.message) # "All sessions released successfully"
```

### Why a Session Ended

Every released or failed session has a `releaseReason`. It is `null` while the session is live, and for sessions created before Steel tracked the reason.

Reading `releaseReason` in TypeScript needs `steel-sdk` 0.21.0 or later. Filtering by it in [Find Past Sessions](#find-past-sessions) needs 0.21.0 or later in TypeScript and Python.

```typescript
const session = await client.sessions.retrieve(sessionId);

console.log(session.status, session.releaseReason);
```

```python
session = client.sessions.retrieve(session_id)

print(session.status, session.release_reason)
```

| `releaseReason` | Meaning |
|---|---|
| `user_requested` | You released the session through the API or SDK. |
| `timeout` | The session reached its `timeout`. |
| `inactivity_timeout` | The session had no activity for its `inactivityTimeout`. |
| `creation_timeout` | The session did not start in time. |
| `startup_failed` | Steel could not start the browser. |
| `browser_closed` | The browser or agent closed itself. This is not a crash. |
| `browser_crashed` | The browser crashed, or its machine stopped responding. |

### Find Past Sessions

`sessions.list` returns sessions newest first. Filter it by status, release reason, or creation time.

```typescript
for await (const session of client.sessions.list({
  releaseReason: 'timeout',
  createdAfter: '2026-11-01T00:00:00Z',
  createdBefore: '2026-11-02T00:00:00Z',
})) {
  console.log(session.id, session.createdAt);
}
```

```python
for session in client.sessions.list(
    release_reason="timeout",
    created_after="2026-11-01T00:00:00Z",
    created_before="2026-11-02T00:00:00Z",
):
    print(session.id, session.created_at)
```

- The list leaves out failed sessions unless you pass `status` or `releaseReason`. Pass `status: 'failed'` to list failed sessions.
- `releaseReason` leaves out live sessions. Without `status`, it includes failed sessions with that reason.
- `createdAfter` includes sessions created at that exact time. `createdBefore` does not. Both take ISO 8601 timestamps.
- `totalCount` comes back only for `status: 'live'` with no metadata, signal, release reason, or date filter. For other lists, follow `nextCursor` until it is `null`. The SDK does this for you.

To filter by your own keys or by what happened during the session, see [Session Metadata](/overview/sessions-api/session-metadata) and [Session Signals](/overview/sessions-api/session-signals).

### Need help building with the Sessions API?
Reach out to us on the **#help** channel on [Discord](https://discord.gg/steel-dev) under the ⭐ community section.

### FAQ

### How long does a Steel browser session stay alive by default?

5 minutes — after that the session is automatically released. You can change this by passing the `timeout` parameter (in milliseconds) when creating the session, for example `timeout: 600000` for 10 minutes.

### What is the maximum length of a Steel session?

Launch sessions can last up to 15 minutes, Scale sessions up to 1 hour, and Enterprise sessions up to 24 hours. Sessions are billed by the minute, rounded up. See [Pricing/Limits](/overview/pricinglimits) for current rates and limits.

### Can I extend the timeout of a running Steel session?

No — Steel currently doesn't support editing the timeout duration of a live session, so set the `timeout` you need at creation time.

### How do I avoid paying for an idle Steel session?

Set `inactivityTimeout` so the session releases itself if your side goes quiet — for example when your agent crashes, hangs, or simply stops sending commands. Without it (the default), a stalled client keeps the session alive and billed until the hard `timeout` cap; with it, any CDP command, Commands API request, or remote input resets the timer, so normal automation is unaffected while you're protected from paying for a browser nobody is driving.

### How do I end a Steel session before it times out?

Call `client.sessions.release(session.id)` — releasing explicitly is best practice rather than waiting for the timeout. To clean up everything at once, `client.sessions.releaseAll()` releases all active sessions.

### How do I find Steel sessions that failed or timed out?

Call `client.sessions.list({ status: 'failed' })` for failed sessions, or filter by `releaseReason`, such as `timeout` or `browser_crashed`. The default list leaves failed sessions out. Each session's `releaseReason` tells you why it ended.
