A Browser Use profile lets a new browser reuse a previous login. Log in with the profile attached, stop the browser to save its state, and pass the same profile ID to your next run. The website still decides how long that login remains valid.
In this tutorial, we'll set up Browser Use authentication with a dedicated demo profile, verify access across two separate runs, and do the same with Playwright's storage state. You'll need Python, a Browser Use API key, and credits for the cloud runs. We'll use a public test account, so you don't need to import your personal browser profile.
TL;DR
- Create an empty Browser Use profile.
- Log in and stop the browser to save its state.
- Start a new run with the same profile ID.
- Check authenticated access and renew the login when needed.
How It Works
After you log in, a website usually recognizes your browser through cookies or tokens in browser storage. A Browser Use profile preserves cookies and local storage so another cloud browser can use them.
A profile is different from an agent session. The session holds the conversation, while a profile holds browser state. Continuing a conversation can reuse a browser that's still running, so two tasks in the same browser don't prove that a saved login survived a restart.
We'll explicitly stop the first browser and start a new conversation for the second run. The only shared input will be the profile ID.
Create a Demo Profile
- Open Configuration -> Profiles.
- Click Create Profile.
- Enter a name such as
Login reuse tutorial. - Choose Start from Scratch.
- Copy the new profile's ID.
The new profile should show no stored cookie domains. Use this empty profile for the tutorial to keep your personal accounts separate. The API key and profile must belong to the same project.
Create a project directory, set up a virtual environment, and install the dependencies:
$ python3 -m venv venv
$ source venv/bin/activate
(venv)$ python -m pip install browser-use-sdk==3.12.1 \
playwright==1.62.0 python-dotenv==1.2.2Create a .env file:
BROWSER_USE_API_KEY=your_browser_use_api_key
BROWSER_USE_PROFILE_ID=your_demo_profile_idAdd these entries to .gitignore:
.env
venv/
playwright/.auth/Our target is The Internet's login page, which publishes its test username and password. After signing in, it opens /secure with a Secure Area heading and a Logout link. These give us concrete checks for the second run.
Log In and Save the Profile
Save this as browser_use_profile.py beside .env. It logs in with the demo profile, waits for the result, and stops the browser to save its state.
import os
from browser_use_sdk.v4 import BrowserUse
from dotenv import load_dotenv
load_dotenv()
task = (
"Open https://the-internet.herokuapp.com/login and log in "
"with the public demo credentials shown there. "
"Confirm the Secure Area heading is visible. Do not log out."
)
with BrowserUse() as client:
run = client.runs.create(
task,
model="bu-fast",
browser_settings={"profileId": os.environ["BROWSER_USE_PROFILE_ID"]},
)
result = client.runs.wait_for_completion(run.id)
print(result.result)
browsers = client.browsers.list(agent_session_id=run.session_id)
for browser in browsers.items:
if browser.status.value == "active":
client.browsers.stop(browser.id)Run it once to log in and save the profile:
(venv)$ python browser_use_profile.pyThe task uses the public credentials displayed on the test site. For a real account, use an appropriate credential flow, such as 1Password, rather than putting a private password in the task.
The script passes profileId through the V4 browser settings. Stopping the browser saves its state to that profile. Wait for the script to finish before starting the next run. If execution is interrupted, stop the browser in the dashboard before continuing.
Don't click Logout before stopping the browser: that would save the site's logged-out state. Closing a browser and signing out of a website are different actions.
Reuse the Login in a Second Run
Keep the same profile ID in .env. Replace only the task assignment in browser_use_profile.py with:
task = (
"Open https://the-internet.herokuapp.com/secure. "
"Do not log in. If redirected to login, report LOGIN_REQUIRED "
"and stop. Otherwise confirm the Secure Area heading is visible."
)Run the script again:
(venv)$ python browser_use_profile.pyEach invocation creates a new agent conversation because the script doesn't pass a session_id. The first browser has already stopped, so this run must load the saved profile into a fresh browser.
The reuse task contains no credentials and has no login fallback. If the site redirects to /login, it should report LOGIN_REQUIRED and stop.
In our November 15, 2026 test, both runs reached the protected page. We also connected to each running browser to check its URL, heading, and Logout link independently of the agent's answer:
| Check | First run | Second run |
|---|---|---|
| Browser ID | a9ec014b... | 23addfdc... |
| Page URL | /secure | /secure |
| Secure Area heading | Visible | Visible |
| Logout link | Visible | Visible |
| Login form filled | Yes | No |
| Browser status after cleanup | Stopped | Stopped |
Browser IDs are abbreviated here. The second run's action trace showed navigation to /secure and page checks, with no credential entry. This demonstrates reuse across separate browser lifetimes on the demo site; it doesn't guarantee that another website will accept a transferred login.
For your own check, inspect the run's browser actions and final response. Confirm the protected URL and an authenticated element, then check the expected account identity before doing account-specific work.
As a control, select a second empty profile and run the script again with the second task. It should reach the login requirement. Our empty-profile cloud run did exactly that. Switch back to the original demo profile afterward; the control doesn't need a login.
Save Playwright Authentication State
With local Playwright, you can save authentication state to a JSON file and load it into a new browser context. The JavaScript API calls this storageState; Python uses storage_state.
Install Chromium:
(venv)$ python -m playwright install chromiumFirst, save this as playwright_login.py. It logs into the public demo account and writes the authenticated state to a file:
from pathlib import Path
from playwright.sync_api import expect, sync_playwright
Path("playwright/.auth").mkdir(parents=True, exist_ok=True)
with sync_playwright() as pw:
browser = pw.chromium.launch()
context = browser.new_context()
page = context.new_page()
page.goto("https://the-internet.herokuapp.com/login")
page.get_by_label("Username").fill("tomsmith")
page.get_by_label("Password").fill("SuperSecretPassword!")
page.get_by_role("button", name="Login").click()
heading = page.get_by_role("heading", name="Secure Area", exact=True)
expect(heading).to_be_visible()
context.storage_state(path="playwright/.auth/demo.json")
browser.close()Next, save this as playwright_reuse.py. It launches a fresh browser, loads that file, and opens the protected page without entering credentials:
from playwright.sync_api import expect, sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch()
context = browser.new_context(storage_state="playwright/.auth/demo.json")
page = context.new_page()
page.goto("https://the-internet.herokuapp.com/secure")
heading = page.get_by_role("heading", name="Secure Area", exact=True)
expect(heading).to_be_visible()
browser.close()Run them separately, from the same directory:
(venv)$ python playwright_login.py
(venv)$ python playwright_reuse.pyBoth scripts finish without an assertion error when authentication succeeds. The first checks the Secure Area heading before saving; the second checks it after loading the saved state. A redirect to the login page fails that check.
Playwright's authentication state includes cookies and local storage. If your application stores authentication in IndexedDB, opt in when saving:
context.storage_state(path="playwright/.auth/demo.json", indexed_db=True)sessionStorage requires separate handling. A storage-state file is also different from a persistent browser user-data directory: it doesn't save every tab, extension, or browser setting. Choose the state your application actually needs.
Handle Expired Cookies
Saved state doesn't make a login permanent. A cookie can expire, the server can revoke its session, or the website can demand another verification step after a device or network change. Browser Use's profile guidance describes these limits.
Start each job by checking a protected page or the expected account identity. If the saved login fails:
- Stop the normal workflow and report that authentication is required.
- Complete the website's supported login and verification flow.
- Save the updated state: stop the Browser Use browser or write a new Playwright state file.
- Retry a small read-only check before continuing the job.
For this demo, restore the first task and run the cloud script with the same profile ID, then repeat the second task. Locally, rerun playwright_login.py to replace the saved JSON file, then run playwright_reuse.py to check it.
To test the failure path locally, load an empty state into a new context and open /secure:
context = browser.new_context(storage_state={"cookies": [], "origins": []})
page = context.new_page()
page.goto("https://the-internet.herokuapp.com/secure")
expect(page).to_have_url("https://the-internet.herokuapp.com/login")This check passed in our test. It simulates missing authentication state; we didn't wait for cookies to expire naturally. Editing a cookie's expiry timestamp isn't a way to renew a session the server has rejected.
Keep User Profiles Separate
Use a different Browser Use profile for each end user. Your application can maintain a mapping like this:
| Application user | Browser Use profile | Local Playwright file |
|---|---|---|
| Alice | Alice's profile ID | playwright/.auth/alice.json |
| Bob | Bob's profile ID | playwright/.auth/bob.json |
These are illustrative accounts. The demo above verifies login persistence and an empty-profile control, not isolation between two real customer accounts.
Look up the profile from the authenticated application user on your server. Don't let a request choose an arbitrary customer's profile ID. Before making changes on the destination website, verify the visible account or workspace and the login state.
Separate browser contexts don't isolate data on the website's server if they use the same account. Two workers sharing one account can still modify the same records. As a practical default, serialize jobs that update one profile, or use distinct accounts and profiles when the jobs need independent state.
Troubleshooting
| Symptom | What to check |
|---|---|
| The second run reaches the login page | Confirm the same profile ID was used and the first browser stopped after successful login. Check whether the site rejected or expired the session. |
| It works in a follow-up but fails in a new conversation | The follow-up may have reused the live browser. Repeat the stop-and-restart check. |
| The profile can't be loaded | Use an API key from the profile's project. A conversation ID or workspace ID isn't a profile ID. |
| Playwright can't find the state file | Run playwright_login.py first and use the same working directory for playwright_reuse.py. |
| State loads, but authentication is missing | Check the origin and storage mechanism. IndexedDB needs opt-in; sessionStorage isn't saved by storage_state. |
| The wrong user appears | Check the application-user-to-profile mapping and stop before performing account-specific actions. |
| A saved login triggers another verification step | Complete the site's supported verification flow. A saved cookie doesn't bypass its security policy. |
Production Tips
Treat saved cookies and state files like credentials. Keep them out of git, public screenshots, and logs, and restrict who can use the profiles or read the files. The demo password is public; real account credentials aren't.
Keep automation profiles dedicated to their accounts and purpose. Validate authentication before doing work, limit recovery attempts, and surface a clear request for reauthentication instead of repeatedly submitting the same failed login.
Stop unused browsers explicitly. In this example, stopping both bounds the browser lifetime and completes the profile-save step needed by the next run.
Conclusion
You now have two ways to reuse a valid login: a Browser Use profile across cloud runs, or a Playwright state file across local executions. Verify access in a fresh browser, keep users separate, and renew authentication when the website requires it. See the Browser Use profile documentation for the profile settings used here.



