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

# Browser Profiles

> Persist browser state across sessions

<Tip>
  STOP. Do not read past this section until you have read and followed [/quickstart.md](/quickstart.md).

  Goal: produce a reliable SDK automation script. The intended authoring flow is generate then edit, not write browser automation from scratch.

  Do not hand-write SDK code with element IDs, selectors, or form fields from memory. Element IDs, selectors, and field mappings must come from a live `observe()` call, CLI `notte page observe` output, or generated workflow code. Guessed browser targets fail on real pages.

  Use the Notte skill and CLI to inspect the live site, validate actions/selectors, handle auth/session state, and export workflow code with `notte sessions workflow-code` before using SDK docs or SDK code.

  SDK reference pages are for understanding, running, or editing generated workflow code. They are not the starting point for manually authoring the initial browser automation. SDK-first code is guesswork on real-world pages with dynamic selectors, auth state, CAPTCHAs, and anti-bot behavior.
</Tip>

Profiles let you save and reuse browser state—including cookies, local storage, session storage, and cache—across multiple sessions. This is useful for maintaining login states, preserving user preferences, or building realistic multi-session workflows.

## How Profiles Work

A profile is a saved snapshot of a browser's user data directory. By default, each Notte session uses a fresh browser state to ensure isolation.

When you create a profile and use it in a session with `persist=True`, Notte saves that session's browser state. You can then attach the profile to future sessions to restore the saved state.

## Create a Profile

Create a profile to store browser state. Optionally, give it a name to keep track of different profiles:

{/* @sniptest testers/sessions/profiles/create_profile.py */}

```python create_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Create a profile with optional name
profile = client.profiles.create(name="my-profile")

print(f"Profile created: {profile.profile_id}")
print(f"Profile name: {profile.name}")
```

## Use a Profile in a Session

Attach a profile to a session to load saved browser state:

{/* @sniptest testers/sessions/profiles/use_profile.py */}

```python use_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Use an existing profile in a session
with client.Session(profile={"id": "notte-profile-abc123", "persist": False}) as session:
    # Session loads saved browser state from the profile
    session.execute(type="goto", url="https://example.com")
```

### Persist Changes

The `persist` parameter controls whether session changes are saved back to the profile:

* `True` - Save browser state to the profile when the session ends
* `False` (default) - Use the profile as read-only; don't save changes

<Note>
  Set `persist=True` the first time you use a new profile to save the initial browser state. After that, you can use `persist=False` to reuse the profile without modifying it.
</Note>

## Profile Login Example

### Step 1: Login and Save to Profile

First, create a profile and perform a login. When the session ends with `persist=True`, all cookies and browser state are saved to the profile:

{/* @sniptest testers/sessions/profiles/persist_profile.py */}

```python persist_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Create a new profile
profile = client.profiles.create(name="github-login")

# Use profile with persist=True to save browser state
with client.Session(profile={"id": profile.profile_id, "persist": True}) as session:
    # Navigate and perform login
    session.execute(type="goto", url="https://github.com/login")
    session.execute(type="fill", selector='input[name="login"]', value="username")
    session.execute(type="fill", selector='input[name="password"]', value="password")
    session.execute(type="click", selector='input[type="submit"]')
    # Browser state is saved to profile when session ends
```

### Step 2: Reuse the Saved Profile

Use the saved profile in a new session. The browser automatically loads the saved state, so you're already authenticated:

{/* @sniptest testers/sessions/profiles/reuse_profile.py */}

```python reuse_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Reuse a previously saved profile (read-only)
with client.Session(profile={"id": "notte-profile-abc123", "persist": False}) as session:
    # Navigate directly - already authenticated!
    session.execute(type="goto", url="https://github.com")

    # Perform actions as logged-in user
    obs = session.observe()
    print(f"Page title: {obs.metadata.title}")
```

**What happens:**

1. A new session is created using the same profile ID
2. The browser loads saved cookies and state from the profile
3. You navigate directly without needing to log in again
4. The session picks up right where the previous one left off

<Note>
  This pattern is useful for:

  * Avoiding repeated logins across automation runs
  * Testing authenticated user flows
  * Managing multiple accounts with separate profiles
  * Maintaining session state over time
</Note>

## Using Profiles with Agents

Profiles work seamlessly with Notte Agents. An agent running with a pre-authenticated profile can access authenticated resources immediately:

{/* @sniptest testers/sessions/profiles/profile_with_agent.py */}

```python profile_with_agent.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Use a profile with an agent
with client.Session(profile={"id": "notte-profile-abc123", "persist": False}) as session:
    agent = client.Agent(session=session, max_steps=10)

    # Agent runs with pre-authenticated session
    response = agent.run(
        task="Go to my GitHub notifications and summarize them", url="https://github.com/notifications"
    )

    print(f"Agent response: {response.answer}")
```

## Managing Profiles

### List Profiles

Retrieve all your profiles with optional filtering:

{/* @sniptest testers/sessions/profiles/list_profiles.py */}

```python list_profiles.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# List all profiles
profiles = client.profiles.list()

for profile in profiles:
    print(f"- {profile.name}: {profile.profile_id}")

# Filter by name
filtered = client.profiles.list(name="github")
print(f"Found {len(filtered)} profiles matching 'github'")
```

### Get Profile Details

Retrieve information about a specific profile:

{/* @sniptest testers/sessions/profiles/get_profile.py */}

```python get_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Get profile details
profile = client.profiles.get("notte-profile-abc123")

print(f"Profile ID: {profile.profile_id}")
print(f"Profile Name: {profile.name}")
print(f"Created At: {profile.created_at}")
```

### Delete a Profile

Delete a profile when you no longer need it:

{/* @sniptest testers/sessions/profiles/delete_profile.py */}

```python delete_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Delete a profile
deleted = client.profiles.delete("notte-profile-abc123")

if deleted:
    print("Profile deleted successfully")
```

## Read-Only Profile Usage

Load a profile without saving changes by setting `persist=False`:

{/* @sniptest testers/sessions/profiles/use_profile.py */}

```python use_profile.py theme={null}
from notte_sdk import NotteClient

client = NotteClient()

# Use an existing profile in a session
with client.Session(profile={"id": "notte-profile-abc123", "persist": False}) as session:
    # Session loads saved browser state from the profile
    session.execute(type="goto", url="https://example.com")
```

**Use cases for read-only profiles:**

* Test workflows without affecting saved state
* Run parallel sessions with the same profile safely
* Maintain a clean baseline profile for repeated use

<Note>
  By default, `persist` is `False`, so you don't have to explicitly specify it for read-only access.
</Note>

## Best Practices

### Profile Management

* Use descriptive names to identify profiles by their purpose (e.g., `github-login`, `twitter-bot-1`)
* Create separate profiles for different accounts or services
* Periodically verify that saved sessions are still valid

### Security

* Profiles contain sensitive session data; treat profile IDs as secrets
* Delete profiles for accounts that are no longer needed
* Use separate profiles for production and development environments

### Performance

* Profiles with large caches may take longer to load
* Consider creating fresh profiles periodically if performance degrades

## Comparison: Profiles vs Cookies

| Feature         | Profiles                                     | Cookies                |
| --------------- | -------------------------------------------- | ---------------------- |
| **Scope**       | Full browser state (cookies, storage, cache) | Cookies only           |
| **Storage**     | Managed by Notte                             | JSON file you manage   |
| **Persistence** | Automatic on session end                     | Manual export/import   |
| **Use case**    | Long-term state preservation                 | Quick cookie injection |

Use profiles when you need complete browser state preservation. Use cookies when you only need to inject specific authentication tokens.

## Next Steps

<CardGroup cols={2}>
  <Card title="Session Lifecycle" icon="rotate" href="/features/sessions/lifecycle">
    Manage session lifecycle
  </Card>

  <Card title="Cookies" icon="cookie" href="/features/sessions/cookies">
    Manual cookie management
  </Card>

  <Card title="Vaults" icon="lock" href="/concepts/vaults">
    Secure credential storage
  </Card>

  <Card title="Session Configuration" icon="gear" href="/features/sessions/configuration">
    All session options
  </Card>
</CardGroup>
