Keep the logic minimal
The shortest script that gets the result is the best one. It’s faster, it fails in obvious ways, and it gives sites the least to notice. Every retry loop, event listener, request interceptor, “stealth” helper or defensive wait is a place for bugs to hide and a signal that this isn’t a person. Add a mechanism after a specific site has shown you that you need it, and add the smallest one that works.Go fast, then verify
Before writing browser steps, ask whether the result is reachable faster:- Go straight to the URL that has the result instead of clicking through navigation.
- Do several pages in one session. Creating a session is the expensive step.
- When start-up itself is the bottleneck (a request handler, an agent loop), keep a browser pool warm and acquire from it.
- Sign in once on a persisted profile and reuse it.
- No fixed sleeps. Rely on the framework’s auto-waiting and stop the session the moment you have the result.
Use Patchright
Stock Playwright and Puppeteer enable CDP domains (Runtime.enable, console and binding hooks) that anti-bot vendors fingerprint. Patchright is Playwright with those removed and the same API. Use it for every Driver integration. Use stock Playwright only if you’ve measured that your target doesn’t care.
Keep the browser’s defaults
- Reuse
browser.contexts()[0]andcontext.pages()[0]. The browser opened with a context and a tab already. - Keep the default viewport, user agent, locale, permissions and flags. Set
windowSize,country,timezoneandlanguageon the create call instead; Driver keeps them coherent with the network the session egresses from. - Don’t add timeouts and navigation settings until a specific site needs them.
Never launch a browser
Driver provides the browser. Connect to the returnedcdpUrl. Not in a Driver integration:
chromium.launch(),chromium.launchPersistentContext(),puppeteer.launch()playwright install, or any browser download- invented
localhostCDP ports or CDP hosts Driver didn’t return
Keep CDP hooks narrow
Broad listeners, request routing and injected scripts make the browser behave unusually, and that’s detectable. Use them only when the task genuinely needs them. Avoid by default (Playwright and Patchright names, Puppeteer equivalents in brackets):page.on(...),context.on(...),browser.on(...)page.route(...),context.route(...)(page.setRequestInterception(true))page.exposeBinding(...),page.exposeFunction(...)(page.exposeFunction)page.addInitScript(...),context.addInitScript(...)(page.evaluateOnNewDocument)- Puppeteer’s
page.setUserAgent,page.emulateTimezone,page.emulate,page.setViewport: fingerprint patches, see below
page.goto, page.click, page.fill, page.locator, page.title, page.textContent.
When a hook is unavoidable, listen for one event type, route one URL pattern, and remove it as soon as the step is done.
Don’t patch fingerprints
The point of Driver is that the browser is genuine. Overridingnavigator, the user agent, WebGL, canvas, timezone or locale from your script makes a real browser look fake. If you need a different location, ask for it on the create call.
Debug with the live view first
When a page behaves unexpectedly, open the session’s live view and look before adding hooks or logging every event. You can take the mouse and keyboard yourself, which is often the fastest way to see what a site is doing.Always stop sessions
Wrap browser work intry / finally so the session is stopped even when navigation or extraction fails. Stopping releases the browser and ends billing.
duration that matches the job. Sessions end on their own at the duration (an hour by default), which caps the cost of anything you forget. A browser from a pool is released the same way, in the finally; its lease timeout plays the role of duration.
Reuse logins with profiles
Signing in on every run is slow and looks suspicious. Sign in once under a named profile withpersist: true, then start later sessions with the same name. They open signed in.
Handle the errors that matter
Full list on Errors.
Label your sessions
Passnote on create, or PATCH it later, with something a teammate would recognise (“nightly price check, client X”). It shows in the dashboard’s session list and record.