Skip to main content

Auth profiles

Some pages only show their content to a visitor who has logged in. An auth profile is that visitor: the cookies and storage a browser holds after signing in, uploaded once and named on any request after that. Nothing logs in again, and the password never reaches us.

Uploading one

Sign in to the site in a browser you drive, save its state, and upload it. With Playwright that is one call, context.storage_state(path="state.json"); any tool that writes the same shape will do.

POST /auth_profiles
{
  "name": "Shop admin",
  "storage_state": {
    "cookies": [
      { "name": "session", "value": "…", "domain": ".shop.example", "path": "/",
        "expires": -1, "httpOnly": true, "secure": true, "sameSite": "Lax" }
    ],
    "origins": [
      { "origin": "https://shop.example", "localStorage": [ { "name": "token", "value": "…" } ] }
    ]
  }
}
201 Created
{
  "id": "ap_7Qm2sX9kLw4RtB1nYc8vDf3H",
  "name": "Shop admin",
  "domains": ["shop.example"],
  "signed_out_at": null,
  "last_used_at": null,
  "state_updated_at": "2026-10-25T09:12:44Z",
  "created_at": "2026-10-25T09:12:44Z"
}

storage_state can also be sent as the file's contents, a string. domains are the hosts the profile may be used for, each with the hosts under it. Left out, they are the one site the state holds cookies for. A state exported from a browser often holds other sites' cookies too (analytics, a sign-in provider), and then the upload asks you to name domains, so a profile is only ever used for the sites you chose.

Using one

Name the profile's id as auth_profile on any endpoint but /lighthouse, /scrape included. The page is loaded as that visitor, and everything else about the request stays the same.

POST /markdown
{
  "url": "https://shop.example/orders",
  "auth_profile": "ap_7Qm2sX9kLw4RtB1nYc8vDf3H",
  "sync": true
}

A URL on none of the profile's domains is refused with invalid_auth_profile before anything is fetched, so a profile is never used anywhere but where you meant it to be. It costs nothing extra: the same credits as the request without it.

Staying signed in

Each request starts a fresh browser from the profile's state, so there is no login to repeat and no wait for one: the session is in place before the first byte is requested. Requests made as the same profile run in parallel.

  • What the site renews is kept. Most sites refresh their session cookie as it is used. Whatever a visit is handed back is written to the profile, so a session the site keeps rolling stays alive for as long as you use it.
  • It is used from one place. Sites tie a session to where it is used from, and end one that moves. Every visit made as a profile goes out from the same exit — the one its first visit used — and if that exit is busy, the request fails rather than move the session: retry in a moment.
  • A session that ends is reported. When a site answers with a sign-in form instead of the page, the request fails with The page asked us to sign in before it would show its content. and the profile's signed_out_at says when. Sign in again and upload the new state.
PUT /auth_profiles/{id}
{ "storage_state": { "cookies": [ … ], "origins": [ … ] } }

A new state is a new session: results stored under the old one are deleted, since they may not even be the same account's. name and domains can be changed on their own, which keeps them. GET /auth_profiles lists a project's profiles, GET /auth_profiles/{id} reads one, and DELETE /auth_profiles/{id} removes it along with the results stored for it.

The same can be done from the dashboard: each project's Auth profiles page lists its profiles, shows which ones a site has signed out, and lets an organization admin upload, replace and delete them.

How the state is kept

A signed-in browser's cookies are as good as its password to whoever holds them, and they are treated that way:

  • Encrypted at rest, and decrypted only to load a page you asked for.
  • Write-only. No response — not the profile's, not a result's, not a webhook — ever carries the state back.
  • Used only for the profile's domains, and only by the project it belongs to.
  • Kept out of our logs, and gone with the profile when you delete it.
A project API key can make requests as every profile in that project. Keep profiles in a project of their own if the key will live somewhere you trust less than the account.

Results are the visitor's own

A page somebody is signed in to is theirs, so a result fetched as a profile is stored under that profile alone. A request made as nobody is never handed it, nor is one made as another profile. Repeat the same request as the same profile inside your max_age window and it is a free cache hit like any other; send max_age: 0 when the page must be current.