How-To

How to scrape a website behind a login

There are two real ways to pull data from a site that makes you log in first, and they're not equal. One's fast and brittle. The other's slow and sturdy. Here's how each works, when to reach for which, and how to stay logged in without getting the account flagged.

Disclosure: some links here are affiliate links. If you sign up through them we may earn a commission at no extra cost to you. We only recommend tools we'd use ourselves. Hire a Clawd is our own service.

The data's right there once you sign in. Your orders, your messages, the analytics the site shows you but won't hand you as a file. Getting a script to see the same page is the trick, because point plain requests at a URL behind a login and you get the login screen back, not your data.

The fix is to make your script carry the same proof of login your browser does. Two ways to do that, and most serious jobs use both. But before any code, the part that matters more than the code.

Are you actually allowed to?

This guide is about pulling data you already have a right to see. Your own account. A client's account, with the client's written say-so. A free-signup account on a site whose terms let you take out what's yours. That's the whole scope, and it isn't a throwaway line.

What this guide is not about: logging into someone else's account. Buying, selling, sharing, or guessing credentials. Credential stuffing, which is firing leaked username-and-password pairs at a login form to see what opens. Account takeover. Scraping private data that belongs to other people. None of those are gray areas you can finesse with a better proxy. In a lot of places they're crimes under computer-misuse laws, and no scraping trick makes them not crimes. Simple test: if you'd be fine telling the site's owner exactly what you're doing, you're on solid ground. If the plan only works because nobody knows it's you, stop.

Terms of service carry more weight here than for public-page scraping. When you logged in, you clicked "I agree" to something, and that something usually has words about automated access. Read them. Breaking a ToS is a contract problem, not a criminal one, but it can still get your account closed and your data locked away.

None of this is legal advice. I'm a scraper, not your lawyer. Laws differ by country and state, they change, and the facts of your case decide everything. If the stakes are real, money, a business built on the data, anything you can't afford to get wrong, pay a lawyer for an hour of their time. We laid out the wider picture in is web scraping legal, but that's a map, not a permission slip.

Two ways in, and when to use each

Both approaches start the same way: a real login happens once, with a real browser, run by a real person or a well-behaved script. The difference is what you do after. Reuse the session and you replay its cookie on plain HTTP requests, fast and cheap, no browser after login. Drive the browser and you keep scraping inside the same authed browser, slower, but able to survive JavaScript pages, rotating tokens, and challenges the fast path can't.

Rule of thumb: try the reuse path first. If the data comes back as clean JSON from an API the site's own frontend already calls, you're done in an afternoon. Drop to a real browser when that returns garbage, gets logged out fast, or the login won't complete without running scripts.

Approach 1: grab the session and replay it

When you log in, the site sets something in your browser that says "this request is you." Usually a cookie. Sometimes a token in a header. Find that thing, send it yourself, and the server can't tell your script apart from the tab you logged in with.

Finding it: open DevTools, go to the Application tab, and look under Cookies for the site's domain. One or two cookies do the real work, with names like session, sid, _session, connect.sid, or a __Secure- prefix. Ignore the analytics junk. Not sure which one matters? The Network tab settles it: open the page with your data, find the request that returns it, and read the Cookie header it sent.

Single-page apps often skip the session cookie. They carry a bearer token, a JWT, in an Authorization header and stash it in localStorage. If the data request shows Authorization: Bearer eyJhbGci..., that string is your key, and you send it as a header instead of a cookie:

# Single-page apps often carry a JWT instead of a session cookie.
from curl_cffi import requests

headers = {
    "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "Accept": "application/json",
}
r = requests.get("https://example.com/api/me", headers=headers, impersonate="chrome")

CSRF tokens trip people up. If your reads work but writes come back 403, the site wants an anti-forgery token, usually a header like X-CSRF-Token whose value has to match a cookie the site set. Copy both, send both.

Here's the fast path end to end. It loads a saved cookie jar, the one the browser login further down writes out, and hits an authenticated JSON endpoint through a sticky proxy:

import json
from curl_cffi import requests

# state.json is the storage state saved by the browser login below.
# It holds the cookies the site set once you were authenticated.
with open("state.json") as f:
    state = json.load(f)

jar = {c["name"]: c["value"] for c in state["cookies"]}

session = requests.Session(impersonate="chrome")
r = session.get(
    "https://example.com/api/account/orders",
    cookies=jar,
    headers={"Accept": "application/json"},
    proxies={"https": "http://USER-session-abc123:PASS@gate.example-proxy.com:7777"},
)

if r.status_code in (401, 403):
    raise SystemExit("session died: re-run the browser login to refresh state.json")

for order in r.json()["orders"]:
    print(order["id"], order["total"])

Why try this first: it's fast, and there's no browser to babysit. Why it breaks: cookies expire, sometimes in weeks, sometimes in twenty minutes, and a JWT often dies in fifteen. When requests start coming back 401 or bouncing to the login page, the session's dead, and retrying won't fix it. You log in again and mint a fresh one, which is the next approach, and the reason the two fit together.

Approach 2: drive a real browser and keep the session

When the fast path won't cooperate, stop fighting it and drive a real browser. A real browser runs the site's JavaScript, so rotating tokens, JS-rendered tables, and script-guarded logins all just work, because you're doing exactly what a person does.

Use a browser that doesn't announce itself as automation. Plain Playwright Chromium sets navigator.webdriver and trips detectors on sight. Camoufox is a patched Firefox that hides those tells at the binary level, which counts double on a login page, where sites watch hardest. The full picture of what they score is in how to avoid bot detection.

Log in once and save the session to disk. Playwright calls this the storage state: a JSON file with your cookies and localStorage. Do this run with a visible window so you can clear MFA by hand the first time:

from camoufox.sync_api import Camoufox

PROXY = {
    "server": "http://gate.example-proxy.com:7777",
    "username": "USER-session-abc123",   # sticky session id lives in the username
    "password": "PASS",
}

# Visible window on the first run so you can clear MFA by hand.
with Camoufox(headless=False, geoip=True, humanize=True, proxy=PROXY) as browser:
    page = browser.new_page()
    page.goto("https://example.com/login")

    page.fill("input[name=email]", "you@example.com")
    page.fill("input[name=password]", "your-own-password")
    page.click("button[type=submit]")

    # Do MFA now if the site asks, then wait until you're actually inside.
    page.wait_for_url("**/dashboard", timeout=90000)

    # Cookies + localStorage for the logged-in session, written to disk.
    page.context.storage_state(path="state.json")
    print("saved state.json")

Every run after that skips the login. Load the saved state into a fresh context and go straight to the data:

from camoufox.sync_api import Camoufox

# Same proxy and session settings as the login run above.
with Camoufox(headless="virtual", geoip=True, proxy=PROXY) as browser:
    context = browser.new_context(storage_state="state.json")
    page = context.new_page()

    page.goto("https://example.com/account/orders")
    page.wait_for_load_state("networkidle")

    for row in page.query_selector_all(".order-row"):
        print(row.inner_text())

That state.json is the same file approach 1 reads, which is the sturdy pattern for a big job: browser for the door, to clear MFA and any challenge once, then plain HTTP for the hauling with the harvested cookies.

Don't want to babysit a login session?

Sessions expire, IPs drift, an MFA prompt shows up at 3am. Hire a Clawd is a personal agent that logs in, keeps the session warm, and pulls your data on a schedule, then pings you when it's ready. You get the export. It handles the plumbing.

See plans from $49/mo →

Keep the same IP, or get logged out

Here's the mistake that kills authenticated scrapes: rotating the IP mid-session. For public-page scraping you rotate constantly, a fresh IP per request, to spread load and dodge rate limits. Do that while logged in and you're telling the site your account just teleported from Ohio to Vietnam between two clicks. Sites read that as a stolen-session signal. Best case they log you out. Worst case they flag the account.

So pin one IP for the whole session. Providers call this a sticky session: the same residential exit IP stays yours for minutes or hours instead of rotating every request. You set it with a session id in the proxy username, the USER-session-abc123 bit above. Same id, same IP.

You still want residential IPs, not datacenter ones, because a logged-in account coming from a known cloud range is its own red flag. Residential plus sticky: a real home IP, held steady for the whole session. We cover picking a pool in the residential proxy guide, and the rotate-versus-stick mechanics in rotating residential proxies in Python.

Recommended tool

You'll want sticky residential sessions

Authenticated scraping lives or dies on one thing: the same believable IP for the whole session. A residential pool with sticky sessions gives your logged-in account one steady home IP instead of a rotating cloud address that gets it flagged on the second request.

See our proxy pick →

Affiliate link, at no extra cost to you.

MFA and CAPTCHA live at the login

Notice where the friction sits. It's the login. Once you're in, the site trusts your session and mostly leaves you alone. So spend your effort, and your money, at the door, and only there.

MFA. If your account uses two-factor, the first login needs the code. Run that one with a visible browser and type it in. After you save the session, later runs reuse it and skip MFA, until the session expires. If it's TOTP, the authenticator-app kind, generate the code in your script from the shared secret and drop the manual step. Just don't scatter that secret across machines.

CAPTCHA. Some logins throw a CAPTCHA when the signals look automated. A real browser on a clean residential IP clears the invisible ones most of the time without a click. When a visible puzzle does show up, you can't fingerprint past it, and solving it by hand every run defeats the point. This is where a solving service earns its fee: it takes the challenge, returns a token your script submits, and the login goes through. You pay per solve, and since you only solve at login, not on every data request, the bill stays tiny. We compared the ones worth paying for in the CAPTCHA solver roundup. If the login sits behind Cloudflare, that's its own puzzle, covered in bypassing Cloudflare bot detection.

Recommended tool

For the CAPTCHA at the login door

When a visible challenge blocks the login, a solving service takes it and hands back a token your script submits, so the run doesn't stall on step one. You only solve at login, so pay-per-solve pricing stays cheap.

See the CAPTCHA pick →

Affiliate link, at no extra cost to you.

Rules that keep a login scrape alive

The stuff that separates a scrape that runs for months from one that dies Tuesday:

Skip the setup

The Anti-Detect Scraping Starter Kit

A ready-to-run Camoufox and residential-proxy Python template with the log-in-and-save-session flow, cookie replay, and sticky sessions already wired in. Plus a checklist for the login gotchas on this page, so your first authenticated scrape doesn't get the account locked.

$19 one-time

Get the kit →

FAQ

Is it legal to scrape behind a login?

It depends on whose data it is and what you agreed to. Scraping your own account, or one you're authorized to access, for data you're allowed to see, is a different thing from breaking into someone else's. The first is usually fine. The second, credential stuffing, account takeover, or pulling other people's private data, runs into computer-misuse law and isn't fine anywhere. And the site's terms probably say something about automated access, which you agreed to by logging in. This isn't legal advice. For anything high-stakes, ask a lawyer.

How do I keep a scraping session logged in?

Save the session and reuse it instead of logging in every run. With a browser, that's Playwright's storage state, a JSON file of your cookies and localStorage that you load into each run. With plain HTTP, it's the session cookie you replay on every request. Either way, pin one sticky residential IP so the account doesn't look like it's hopping countries, and log in again only when the session expires.

Why do I get logged out when scraping?

Three usual reasons. One, you rotated the IP mid-session, so the site saw your account jump locations and killed it. Two, the cookie or token expired, which some sites do fast, and you kept using the dead one. Three, your requests looked automated, thin headers, a non-browser TLS handshake, a robotic pace, so the site invalidated the session on purpose. Fixes, in order: sticky IP, re-login when you get a 401 instead of retrying, and send a browser-shaped request so you don't get singled out.

Do I need proxies for authenticated scraping?

For a light job on your own account, often no. Log in, pull your data at a calm pace from your own IP, and you'll usually be fine. You want proxies when the volume climbs, when you're on a datacenter IP a logged-in account shouldn't come from, or when the site is strict about where a session connects from. When you do, reach for sticky residential sessions, not rotating datacenter IPs, so the account stays put on one believable IP.

Or hand the whole login flow to an agent

If keeping a session alive, dodging the logout, and refreshing cookies on a schedule sounds like a second job, that's because it is one. Hire a Clawd runs it for you, around the clock, and drops the data in your inbox or on Telegram. Nothing to maintain.

Get your own agent →