Profiles
Keep agents logged in across sessions, and import your existing Browser Use Cloud profiles so profileId references keep working unchanged.
A profile is a persistent browser storage jar: cookies plus localStorage and sessionStorage per origin, held on disk in Playwright storage-state format. Attach one to a session and the agent starts already logged in to whatever that profile was logged in to, instead of hitting a sign-in wall on every run.
Profiles are the difference between an agent that can read a public listing and one that can work inside an authenticated application.
Create a profile
curl -X POST https://your-host/v3/profiles \
-H "Authorization: Bearer $OPENBROWSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my-profile"}'The response carries the profile id; pass it as profileId when you create a session. A new profile starts with an empty jar, which the first authenticated session fills.
List what exists:
curl https://your-host/v3/profiles \
-H "Authorization: Bearer $OPENBROWSE_API_KEY"Each profile in the response includes its cookieDomains, the domains the jar currently holds cookies for, and lastUsedAt, which updates whenever a session loads the profile. Rename with PATCH /v3/profiles/{id}, delete with DELETE /v3/profiles/{id} (which also removes the jar from disk).
Import from Browser Use Cloud
A cloud profile export is the same storage-state shape, cookies plus per-origin localStorage, so it imports directly. Import one and the local profile id matches the cloud id, so every profileId already in your code keeps working.
From a source checkout, the import script is the shortest route. It creates the profile if it does not exist, normalises the cookies, and backs up any existing jar to .import-bak:
.venv/bin/python -m scripts.import_profiles personal_profile.storage_state.json \
--profile-id <cloud-profile-id> --name "Personal Profile"A bundle (a JSON list of profile entries, or {"profiles": [...]} wrapping one) carries an id per entry, so one command imports many:
.venv/bin/python -m scripts.import_profiles bundle.jsonThat script ships with the repository rather than with the published package, so an installed copy uses the dashboard's Profiles importer or the API below instead. Both do the same work, and the endpoint also creates the profile if the id does not exist yet:
curl -X PUT https://your-host/v3/profiles/<profile-id>/storage-state \
-H "Authorization: Bearer $OPENBROWSE_API_KEY" \
-H "Content-Type: application/json" \
--data @personal_profile.storage_state.jsonVerify with GET /v3/profiles/<id>, or the Profiles page in the dashboard, which lists the imported cookieDomains.
The storage state format
{
"cookies": [
{
"name": "session",
"value": "...",
"domain": "example.com",
"path": "/",
"expires": 1999999999,
"httpOnly": true,
"secure": true,
"sameSite": "Lax"
}
],
"origins": []
}origins carries each origin's localStorage and sessionStorage, which the browser restores on load. When a session ends, the full storage state is written back to the same file, so cookies acquired or refreshed during the run persist, with localStorage preserved. The write-back is locked per profile and shielded against shutdown, so two sessions ending at once, or a restart mid-save, cannot truncate a jar.
These files are live session credentials. Anyone holding one is logged in as you on every site it covers. Never commit them; a source checkout git-ignores data/ for this reason, and an installed copy keeps it out of the repository altogether by writing under ~/.openbrowse instead. Treat a profile export with the same care as a password manager export.
Using a profile
const session = await client.sessions.create({
task: "Open the billing page and return every invoice as structured data.",
model: "claude-sonnet-5",
profileId: "<profile-id>",
outputSchema: mySchema,
});For credentials the agent must type rather than carry as cookies, use sensitiveData alongside the profile; see writing tasks.
One profile, one running session. Two runs cannot hold the same profile at once, because both would write their cookie jar back over the other's. The claim is taken inside the run rather than at request time, so the second session is accepted by the API, reaches running, and only then ends error with a message naming the session that holds the profile. If you need two jobs against one login running together, give them separate profiles.
Listing and organising profiles
GET /v3/profiles returns an envelope rather than a bare array: {items, totalItems, pageNumber, pageSize}, with page and pageSize to walk it and query to search by name. A profile also carries an optional userId, settable on create and on update, which is there to tag whose login a jar represents when one instance serves several people. Nothing in the runtime reads it; it is yours to filter on.
The API reference has the full field list for each endpoint.
Next
Choosing a model covers which model and reasoning effort to pair with the work, and troubleshooting covers what to do when a session fails.
Cost control
What a run costs, how the real-cost engine prices it from actual token usage, and how maxCostUsd and CLOUD_MAX_COST_FACTOR enforce a hard stop-loss.
Solving CAPTCHAs
How OpenBrowse solves CAPTCHAs through CapSolver: which types are solved, which are only recognised, what a solve costs, and why the checkbox never ticks.