How to Manage Multiple Browser Profiles for Scraping Without Getting Blocked
Somewhere around the turn of the decade, the arms race in web scraping quietly changed venues. It used to be a network problem: rotate enough IP addresses and you could pull whatever you wanted. Then Cloudflare, DataDome, Akamai and HUMAN stopped caring quite so much about where a request came from and started grading what sent it. The browser itself became the thing under inspection — its canvas output, its WebGL renderer string, its fonts, its timezone, the tiny tells that automation frameworks leave behind.
Which means the central question for anyone scraping at scale in 2026 is no longer 'how many proxies do I need?' It's how to manage multiple browser profiles for scraping so that each one looks like a distinct, boring, real person on a distinct, boring, real machine — and keeps looking that way run after run.
This guide is the answer I wish someone had handed me years ago. It covers what a browser profile actually is under the hood, why the popular approaches fail, how to design a profile pool that survives contact with modern anti-bot systems, and the operational habits that separate scrapers who run for months from scrapers who burn a fresh setup every week.
Why one browser — or a thousand headless ones — fails
The classic scraping stack was one script, one HTTP client or headless browser, and a rotating proxy pool. Every request went out through a different IP, and for a while that was enough.
It fails now for a reason that's obvious once you see it from the defender's side. If a thousand different IP addresses all present the same browser fingerprint — same canvas hash, same WebGL renderer, same font list, same screen geometry — the anti-bot system doesn't see a thousand visitors. It sees one visitor wearing a thousand hats. Fingerprint correlation across IPs is one of the cheapest, highest-confidence signals a detection vendor has, and rotating proxies actually makes it stronger: no real human teleports between Frankfurt and São Paulo in eleven seconds while keeping an identical GPU.
Headless browsers add their own layer of tells on top:
navigator.webdriveristruewhen a browser is driven by WebDriver-based automation — it's part of the web platform by design, and every serious anti-bot script checks it.- Older headless Chrome announced itself in the user agent, and even the new headless mode differs from a headed browser in subtle ways: no GPU-accelerated rendering by default, different window behaviour, missing UI-driven state.
- DevTools-protocol automation leaves artifacts. Frameworks that call
Runtime.enableto evaluate JavaScript create observable side effects that detection scripts have learned to trip on. Clicks synthesized through JavaScript are untrusted events, and sites can tell.
So the failure mode compounds: an obviously-automated browser, cloned a thousand times, hopping IPs. Each layalone might survive; together, they're a fingerprint that reads 'scraper' at a glance.
What a browser profile actually is
When people say 'profile', they often mean two different things fused into one.
The first is the data directory — Chromium's --user-data-dir. That's a folder on disk holding cookies, localStorage, IndexedDB, cache, service workers, extension state, saved passwords. It's the browser's memory. Two processes pointed at two different data directories share nothing: no cookies, no storage, no cross-contamination. This is real, kernel-level isolation, not a JavaScript trick.
The second is the fingerprint — the set of values that JavaScript running on a page can read about the device. Canvas rendering output, WebGL vendor and renderer strings, AudioContext processing quirks, installed fonts, navigator.hardwareConcurrency, deviceMemory, screen dimensions, timezone, language list, platform, user agent and the Client Hints that accompany it.
A genuinely isolated profile needs both. A shared data directory means one banned account's cookies poison the next session. A shared fingerprint means the sites correlate profiles even when the cookies are pristine. If you want the deeper mechanics of the second half, Browser Fingerprinting Explained for Beginners walks through each surface in plain language, and the EFF's Cover Your Tracks project remains the best free demonstration that a fingerprint really is as identifying as people claim.
The architecture that actually holds up
Here's the mental model I use. A scraping profile is a triple: an identity, a network path, and a state store. Change any one of them independently and you create a contradiction a detector can find.
Identity: one coherent fingerprint per profile
The key word is coherent, not random. Randomising every fingerprint field independently produces devices that don't exist — a MacBook user agent reporting a Win32 platform, an iPhone with 32 GB of RAM, an Nvidia RTX renderer string on a machine claiming to be a Mac. Detection vendors maintain tables of which combinations occur in the wild. An impossible device is more suspicious than a common one, because half the internet genuinely is a Windows 11 laptop on Chrome with an Intel iGPU and that's fine.
A good fingerprint generator enforces internal consistency:
- The user agent, the
Sec-CH-UAClient Hints,navigator.platformandnavigator.oscpuall describe the same OS and the same browser version. - The WebGL vendor/renderer pair is a real pair — an ANGLE string that a real driver on that OS would actually emit.
- Screen resolution, available screen area, device pixel ratio and window size are physically possible together, with the OS's real taskbar/dock inset subtracted.
- Timezone, language list and geolocation agree with the exit IP of the proxy attached to the profile.
hardwareConcurrencyanddeviceMemoryland on values real hardware reports (4, 8, 12, 16 — not 7).
That last cluster matters more than people expect. A German residential IP serving a browser set to America/Chicago with en-US as the only language is a contradiction that costs nothing to detect. Timezone-versus-IP mismatch is one of the first checks in almost every commercial anti-bot rulebook.
There's also a question of where the spoof lives. Injected-JavaScript fingerprinting — overriding HTMLCanvasElement.prototype.toDataURL and friends from a content script — is detectable in several ways: the patched function's toString() output, prototype chain anomalies, timing differences, and the fact that Web Workers get a different view of the world than the main thread unless you patch them too. Spoofing implemented natively inside the browser engine has none of those seams, because there's no patch to find. If you're evaluating tools, that distinction is worth more than any feature list. How to Change Browser Fingerprint goes into the practical differences.
Network path: one proxy per profile, sticky
The second leg of the triple. Every profile gets its own proxy, and — this is the part people skip — that pairing should be durable. Profile 47 uses the same exit IP today that it used last week, or at minimum the same subnet, same city, same ASN.
Why sticky? Because session continuity is itself a trust signal. Real users come back from the same ISP. A profile that has visited a site fourteen times from a Comcast connection in Denver and then shows up on a datacenter IP in Singapore has just told the site something. Rotating proxies underneath a persistent profile is the single most common self-inflicted wound in this business.
Some practical rules:
- Match proxy type to target. Datacenter IPs are fast and cheap and are fine for sites that don't care. Residential and mobile IPs cost more and are necessary where ASN reputation is scored. Don't pay mobile prices for a target that never checks.
- Test the exit before you trust it. A proxy that resolves but leaks your real IP through WebRTC is worse than no proxy, because you've now bound your real address to that profile's fingerprint. WebRTC's ICE candidate gathering will expose local and public addresses unless it's masked to the proxy's exit.
- Keep DNS on the proxy side. Resolving hostnames locally while routing traffic remotely is a leak that shows up in timing analysis and, occasionally, in plain sight.
- Meter bandwidth per profile. Scraping runs die quietly when a proxy plan hits its cap. Per-profile byte accounting turns a mystery outage into a line item.
If you're still deciding whether a proxy alone is enough, Antidetect Browser vs VPN lays out exactly what each layer does and doesn't cover — the short version being that a VPN changes one number and nothing else.
State: persistent data directories, deliberately aged
The third leg. Each profile writes to its own --user-data-dir, and that directory persists between runs.
This is where a lot of scraping setups leave value on the table. A brand-new browser profile with an empty cookie jar, no history, no cached resources and no localStorage is a rare thing on the real web. Most visitors arrive with baggage. Sites that score trust — marketplaces, ticketing, travel, anything with pricing worth scraping — weight returning-visitor signals heavily. A profile that has a two-week-old _ga cookie and a populated HTTP cache clears bars that a fresh one doesn't.
So: warm your profiles. Before you point a profile at the thing you actually want, let it browse for a while. Visit the site's homepage, click into a category, sit on a page for ten seconds, come back tomorrow. It's unglamorous and it works. Treat the data directory as an asset you're building, not a temp folder.
Designing your profile pool
How many profiles, of what kind, doing what? This is a resource-allocation problem and it's worth being deliberate.
Segment by target, not by convenience
Profiles should be grouped by the site they hit. A profile that scrapes Amazon product pages should not also hit LinkedIn and a travel aggregator. Cross-site behaviour is visible to any of them that use a shared detection vendor, and vendors do share signal across their customer base — that's most of the value they sell.
One group per target, sized to the volume you need from that target. Within a group, vary the fingerprints across plausible device distributions: mostly Windows desktops, a meaningful minority of macOS, a slice of Android if the site's traffic mix would have that. Copy the real world's shape.
Warm pool vs disposable pool
I keep two tiers, and you probably should too.
Warm profiles are long-lived. They have history, logged-in sessions where relevant, aged cookies, and a stable proxy. They're expensive to build (days of warming, a good residential IP) and you use them for the high-value, high-scrutiny work: logged-in data, checkout flows, anything behind an account. You do not burn these on bulk page fetches. When one dies, you feel it.
Disposable profiles are cheap and numerous. Fresh fingerprint, datacenter or rotating residential proxy, no meaningful state. They do bulk collection on pages that don't require a session. If one gets blocked you shrug and mint another. The failure rate here should be a metric you watch, not an event you investigate.
The mistake is running one tier for everything. All-warm is slow and expensive. All-disposable can't touch anything gated. Split them and size each to the job.
| Dimension | Warm profiles | Disposable profiles |
|---|---|---|
| Lifespan | Weeks to months | Hours to days |
| Proxy | Sticky residential/mobile | Datacenter or rotating residential |
| State | Aged cookies, cache, logins | Minimal, often discarded |
| Cost per profile | High (IP + warming time) | Low |
| Typical use | Logged-in pages, pricing behind auth, checkout | Public listings, search results, bulk crawl |
| Concurrency | Low — a few per target | High — dozens |
| On block | Investigate; the profile is an asset | Replace; log the rate |
| Fingerprint churn | Never — stability is the point | Fresh per profile |
Sizing and concurrency
A browser profile is a real OS process. Chromium's memory footprint is what it is; plan on roughly 250–400 MB per instance for light pages, more for heavy JavaScript apps. On a 16 GB workstation with low-memory flags enabled you can realistically hold 20–30 concurrent instances before swap ruins your latency. Beyond that you're looking at a bigger box or a distributed setup.
Don't push concurrency to the ceiling. Anti-bot systems look at request cadence per identity as well as per IP. Twenty profiles each making a request every eight seconds is far less remarkable than five profiles hammering at two per second. The pool exists so you can slow each member down.
Naming, grouping and the boring metadata
At fifty profiles you can hold the mapping in your head. At five hundred you cannot, and the day you need to answer 'which profiles are on the ASN that just got range-banned?' you'll wish you'd tagged them.
Minimum viable metadata per profile: target group, proxy identifier and ASN, fingerprint OS family, creation date, last-successful-run timestamp, cumulative block count, and a free-text note. That's enough to answer the questions that actually come up during an incident. Groups matter more than tags because they're how you do bulk operations — launch all of a group, rotate proxies for all of a group, retire all of a group after a bad week.
Automation that doesn't announce itself
Having good profiles and then driving them badly is a common way to lose. The driving layer is where most of the remaining tells live.
Prefer trusted input events
A click dispatched via element.click() or a synthetic MouseEvent from page JavaScript carries isTrusted: false. Any script can read that. A click delivered through the browser's own input pipeline — the DevTools Protocol's Input.dispatchMouseEvent, which enters at the same layer real hardware input does — carries isTrusted: true and is indistinguishable from a human's.
The same holds for typing. Setting input.value directly fires no key events at all, skips the composition pipeline, and produces a text field that was filled instantaneously by nobody. Real typing has inter-key delays with a characteristic distribution, occasional corrections, and focus/blur events in the right order.
Avoid the automation flags entirely
Don't launch with --enable-automation. Don't attach a debugger session you leave open for the whole run if you can avoid it — a persistently attached DevTools client is observable behaviour, and some detection scripts specifically probe for the side effects of Runtime.enable. The Chromium team documents the DevTools Protocol thoroughly enough that both sides of this arms race read the same reference.
The practical pattern: launch the browser as a plain process, with a debugging port open but no client permanently attached, and connect only for the specific operations that need it. Do your DOM work through the domains that don't require script evaluation — DOM, Input, Page, Network — and reach for JavaScript evaluation only when there's genuinely no other way.
Pace like a person
Humans are slow and irregular. They read. They scroll partway down a page and stop. They open a listing, go back, open a different one. They occasionally mis-click. They take a break for lunch.
Your scraper doesn't need to simulate a full behavioural model, but it should avoid the three most machine-like patterns: perfectly uniform intervals, perfect ordering (crawling a paginated list 1, 2, 3, 4… with no deviation ever), and impossible speed (parsing a page and clicking a link 40 ms after DOMContentLoaded).
Add jitter with a realistic distribution — log-normal is closer to human dwell time than uniform. Randomise ordering where the data allows. Include occasional dead-end navigations. Respect a site's robots.txt and its terms; besides the legal and ethical dimension, the targets that punish aggressive scraping hardest are exactly the ones with the best detection.
Handle failures without cascading
When a profile gets a CAPTCHA or a 403, the wrong response is to retry immediately with the same profile, then again, then rotate the proxy and try again. That sequence is a signature. It escalates a soft flag into a hard ban and often takes the IP range with it.
Better: mark the profile as cooling, back off exponentially, let a different profile pick up the work item, and log the failure with enough context to spot patterns. If block rate on a target jumps from 2% to 30% overnight, that's a defence change, and the fix is upstream in your fingerprints or proxies — not in more retries.
One more thing worth internalising: a CAPTCHA is usually about the IP, not the fingerprint. Residential proxy pools recycle addresses, and the person who had yours before you may have been running something nasty. Before you spend a day tuning canvas noise, swap the proxy and see if the CAPTCHAs stop.
Session persistence and portability
If your scraping involves logged-in accounts — and commercial scraping usually does eventually — session handling becomes the hardest operational problem you have.
Capture cookies properly
Reading document.cookie gets you a fraction of the jar. It misses HttpOnly cookies, which is to say it misses most session tokens. You need the browser's own cookie store, which over the DevTools Protocol means Network.getAllCookies or Storage.getCookies — those return the full set including HttpOnly, Secure and partitioned cookies with their real attributes.
And cookies alone aren't the session. Modern web apps keep auth material in localStorage, IndexedDB and sometimes service worker caches. A 'session export' that only carries cookies will restore a half-logged-in state that behaves strangely and often triggers a re-auth challenge — which, on a new device fingerprint, is exactly when accounts get flagged.
The two-machine problem
The moment you run scrapers on more than one machine, you get a distributed-systems problem you didn't ask for. Machine A has profile 12 logged in. Machine B pulls a stale copy of profile 12 and opens it. Now two sessions exist for one account, from two IPs, and when B closes it writes its stale cookies back over A's good ones. The account sees concurrent sessions from different locations — one of the most reliable ban triggers on any platform — and you've also destroyed the working login.
The fixes are the ordinary distributed-state fixes:
- A lock. A profile can be open on exactly one machine at a time. Claim it, heartbeat it, release it. If the lock can't be acquired, don't open.
- Last-writer-wins with provenance. Stamp every session snapshot with a timestamp and the machine that produced it. Never overwrite a newer snapshot with an older one.
- Never push an empty session. If a profile has no cookies — because it never opened, or the capture failed — it must not write a blank session over the good one. This sounds obvious and is the bug everyone ships at least once.
- Verify before opening. If you can't confirm you have the current session, don't launch. A delayed run costs minutes; a clobbered login costs the account.
Dual Login implements all four, which is less a boast than an admission that we learned each one the hard way.
Choosing tooling: what actually matters
The market is crowded and the marketing is largely interchangeable. Here's what I'd actually evaluate, roughly in priority order.
Is the fingerprint applied natively or injected? Native spoofing inside the engine has no patched functions to detect and covers Web Workers, iframes and OffscreenCanvas automatically. Injected JavaScript covers the main thread and hopes. This is the biggest single quality differentiator and the hardest to fake in a demo.
Does each profile really get its own process and data directory? Some cheaper tools multiplex profiles inside one browser using container-style isolation. That's fine for casual multi-accounting and inadequate for scraping — shared process state means shared GPU context, shared memory pressure, and correlation risk.
Can you drive it with your own code? A tool with a beautiful UI and no automation surface is a dead end for scraping. You want a local API, ideally something that speaks the DevTools Protocol, so your existing Playwright or Puppeteer or raw-CDP code can point at it.
What happens when the vendor's cloud is down? If profile launches require a round trip to someone else's server, their outage is your outage. Local-first tooling with optional cloud sync survives this; cloud-only tooling does not.
Where does the data live? Cookies for hundreds of accounts are among the most sensitive things you'll ever store. Know whether they sit on your disk or someone else's, and whether they're encrypted at rest.
What does it cost at your scale? Per-profile pricing gets brutal past a few hundred profiles. Model your real number, not the marketing tier. Cheaper Multilogin Alternatives That Actually Work runs the arithmetic across the main options, and GoLogin vs AdsPower compares the two most commonly shortlisted tools head to head. For smaller operations, Cheap Antidetect Browser for Small Teams narrows it further.
Test before you commit
Whatever you're evaluating, run this checklist against a trial before you pay for a year:
- Launch two profiles. Check on a fingerprinting test page that canvas, WebGL, audio and font signals genuinely differ.
- Check that
navigator.webdriverisfalse, that there's no automation infobar, and that the browser reports a plausiblechrome://version. - Attach a proxy in a different country. Verify timezone,
Intl.DateTimeFormat().resolvedOptions().timeZone, language headers and geolocation all follow the exit IP. - Test WebRTC specifically. It should report the proxy's address, not yours.
- Log into something real, close the profile, reopen it, and confirm you're still logged in.
- Drive it from your automation code and confirm the API shape is stable.
Antidetect Browser Free Trial: What to Test Before You Pay expands this into a full evaluation protocol.
Operating a scraping fleet day to day
The setup is a weekend. The operations are forever. A few habits that pay for themselves.
Instrument block rate per group
The single most useful metric is block rate segmented by profile group and by proxy provider. Everything else is downstream of it. A rising rate in one group means that target changed something; a rising rate across all groups on one provider means the provider's IP ranges are burning.
Track it daily. Alert on deltas, not absolutes — 5% block rate might be normal for a hard target and catastrophic for an easy one.
Retire profiles on a schedule
Disposable profiles should have a maximum age even if they're still working. A profile that has made forty thousand requests to one site over three months is an anomaly no matter how clean its fingerprint is. Rotate them out before they become interesting.
Warm profiles are different — their value is their age — but they should still be audited. If a warm profile has been throwing intermittent challenges for a week, it's already flagged and you're just extending the observation window.
Keep fingerprints stable per profile
Regenerating a profile's fingerprint is almost always the wrong move. From the site's perspective, the same cookie jar suddenly reporting a different GPU is a device swap, and device swaps on established sessions are a classic account-takeover signal. If a profile is compromised, retire it and build a new one. Don't reshuffle its identity and keep the cookies.
Separate scraping from account operations
If you also run accounts on the platforms you scrape — sellers, advertisers, affiliates — keep those profiles in a completely different pool with different proxies. A block on a scraping profile is a rounding error. A block on an account you make money from is not. The playbooks in How to Avoid Account Bans on Amazon Seller and How to Manage Multiple Facebook Accounts Safely cover the account side, which has meaningfully different risk economics from bulk collection.
Version your configuration
Fingerprint generation rules, proxy assignments, pacing parameters, group definitions — treat them like code. When your block rate doubles on a Tuesday, the first question is 'what changed on Monday?' and you want an answer that isn't guesswork.
A worked example: a mid-size price-monitoring setup
To make this concrete, here's roughly how I'd stand up price monitoring across four retail sites, pulling a few hundred thousand product pages a month.
Groups. Four, one per retailer. No profile crosses groups.
Sizing. Retailer A is aggressive about detection and needs logged-in pricing: 12 warm profiles on sticky residential IPs, matched to the retailer's core market. Retailers B, C and D are public-catalogue only: 40 disposable profiles each, datacenter IPs, replaced weekly.
Fingerprints. Device mix per group weighted to that retailer's actual audience — for a US electronics site, roughly 60% Windows desktop, 20% macOS, 20% Android, with resolutions drawn from real-world distributions rather than uniformly random.
Warming. The twelve warm profiles get two weeks of light use before they touch production work: homepage, a few categories, a search, occasional multi-day gaps. Logins happen at the end of warming, never on day one.
Pacing. Warm profiles: one page every 20–90 seconds, log-normal, with a two-hour quiet window overnight in the profile's own timezone. Disposables: one page every 5–15 seconds, capped at 400 pages per profile per day.
Failure handling. Any 403 or CAPTCHA cools the profile for six hours and re-queues the item. Three failures in a day retires a disposable profile permanently. Any failure on a warm profile pages a human.
Monitoring. Block rate per group, per day. Bandwidth per profile. Session-validity check on warm profiles every morning — load one authenticated page, confirm the login held.
Nothing exotic. It runs because each piece is boring and consistent, which is the whole point.
Common mistakes, ranked by how much they cost
- Rotating proxies under a persistent profile. Instantly contradicts the profile's own history. The most expensive and most common error.
- Reusing one fingerprint across many profiles. Turns your whole fleet into one identity in the detector's eyes; when one dies they all do.
- Impossible fingerprint combinations. A device that can't exist is worse than a common one.
- Timezone/language not matching the exit IP. Free to fix, trivially detectable.
- Immediate retry after a block. Escalates soft flags into hard bans and burns the IP range.
- Pushing an empty session over a good one. Silently destroys logins across every machine you sync to.
- No per-group metrics. You find out something broke a week after it broke.
- Treating CAPTCHAs as a fingerprint problem. Usually it's the IP; you waste days in the wrong place.
Where Dual Login fits
We built Dual Login because the tools we were using each got one of these things right and something else badly wrong.
Every profile runs as its own OS process with its own --user-data-dir, so isolation is enforced by the operating system rather than by a JavaScript sandbox. Fingerprints are applied natively inside a custom Chromium build — read from a signed, encrypted config bound to that profile's data directory — so there is no patched function for a detection script to find, and the spoof reaches Web Workers and OffscreenCanvas the same as the main thread. Generation enforces internal consistency across UA, Client Hints, platform, WebGL strings, screen geometry and timezone.
Proxies attach per profile with SOCKS and authenticated-HTTP bridging, WebRTC masked to the exit IP, and per-profile bandwidth accounting. Automation runs over raw DevTools Protocol using only DOM, Input, Page and Network — trusted input events, navigator.webdriver stays false, and no permanently attached debugger. Sessions capture the full cookie jar plus localStorage and IndexedDB, with cross-machine locking and last-writer-wins provenance so two PCs can't clobber each other's logins.
And it runs locally. The engine is on your disk, your cookies are on your disk, and cloud sync is optional rather than load-bearing. If our servers have a bad day, your scrapers don't.
If you want the general case rather than the scraping-specific one, What Is an Antidetect Browser and How Does It Work is the primer, and Best Antidetect Browser for Multiple Accounts covers the account-management angle.
FAQ
How many browser profiles do I need for scraping?
Work backwards from your target volume and a safe per-profile rate. If you need 100,000 pages a month from a site where one page every 30 seconds is safe, that's about 2,800 pages per profile per month at eight hours of activity a day — so roughly 36 profiles, plus 20–30% headroom for cooling and retirement. Fewer, slower profiles beat many fast ones almost every time.
Can I just use Chrome profiles instead of an antidetect browser?
Chrome's built-in profiles give you separate cookie jars, which solves state isolation but nothing else. Every Chrome profile on one machine shares the same canvas output, WebGL renderer, font list, screen geometry and hardware values — so they're trivially correlated as one device. Chrome profiles are fine for keeping work and personal logins apart; they don't survive scraping-grade detection.
Do I need a different proxy for every profile?
For anything with real detection, yes — and it should be sticky, not rotating. You can share an IP across a small number of profiles if they hit different sites, but two profiles on the same target from the same IP with different fingerprints looks like one machine running multiple identities, which is precisely what it is.
Is headless mode safe for scraping in 2026?
Chrome's new headless mode is much closer to headed than the old one, but differences remain — GPU rendering behaviour, window management, and various platform APIs. On well-defended targets, run headed browsers on a real or virtual display. On sites that don't check, headless is fine and cheaper. Test both against your specific target rather than assuming.
Why am I getting CAPTCHAs even with a perfect fingerprint?
Almost always the IP's reputation, not the fingerprint. Residential proxy pools recycle addresses and you inherit whatever the last tenant did. Swap the proxy and re-test before touching anything else. Request cadence is the second suspect — too fast, too regular, or too many requests from one identity.
How do I keep sessions alive across multiple machines?
Capture the full session — cookies via the browser's cookie store (not document.cookie), plus localStorage and IndexedDB — stamp it with a timestamp and origin machine, and enforce a lock so a profile can only be open in one place at a time. Never write an empty or older snapshot over a newer one. Without those guarantees, two machines will eventually destroy a login between them.
Wrapping up
The skill in modern scraping isn't finding a clever bypass. It's building a fleet of identities that are individually unremarkable and collectively uncorrelated, then operating them patiently enough that nothing about them stands out. Coherent fingerprints, sticky proxies, persistent and aged state, trusted input, human pacing, and metrics good enough to tell you when the ground shifts.
Do those six things and you'll spend far less time fighting blocks than the people looking for a magic flag.
If you want to see what this looks like with the plumbing already built — native fingerprinting, per-profile processes and proxies, and a local automation API you can point your existing scripts at — Dual Login is worth an hour of your time. Create a couple of profiles, run them through the test checklist above, and judge it on what the fingerprinting pages actually say.