Best Browser Automation Tools for Testing and Web Tasks
Compare Playwright, Selenium, Puppeteer, and Cypress by browser coverage, language, debugging, and scale. Pick a practical starting point for your workflow.
The best browser automation tool depends on the browsers you must support, your team’s language, and whether you need a test framework or flexible browser control. For a new project that needs one framework across Chromium, Firefox, and WebKit, Playwright is a sensible default to evaluate first: its official documentation describes those engines plus an integrated test runner with auto-waiting, assertions, tracing, isolation, and parallel execution. That is a recommendation inferred from documented features, not a measured claim that it is fastest or best for every team.
Choose Selenium when WebDriver interoperability, existing Selenium skills, or distributed browser infrastructure matter; Puppeteer for JavaScript automation centered on Chrome or Firefox; and Cypress for test-focused workflows after checking its current browser matrix. Cypress currently describes WebKit as experimental.
Quick comparison
| Tool | Good fit | Browser and language notes | Check before choosing |
|---|---|---|---|
| Playwright | Cross-browser tests, scripts, and agent workflows with an integrated runner | Chromium, Firefox, WebKit; TypeScript, Python, .NET, Java | Playwright WebKit is not branded Safari. Validate platform-specific behavior and branded Chrome or Edge requirements. |
| Selenium | WebDriver-based automation, existing Selenium suites, broad ecosystem, distributed runs | Language-specific bindings and WebDriver implementations for major browsers | Plan for driver, browser, and Grid infrastructure that fits your environment. |
| Puppeteer | JavaScript browser control, UI tests, forms, screenshots, PDFs, performance traces | Chrome and Firefox; headless by default, configurable headful | Its documented browser set does not include WebKit. |
| Cypress | Browser testing with a test-oriented workflow | Chrome, Firefox, Edge, and experimental WebKit are described in its browser docs | Review WebKit limitations before treating it as Safari-engine coverage. Cypress also documents Electron as deprecated as a test browser and planned for removal. |
These tools’ official documentation describes different strengths; it does not establish a neutral, comparable speed ranking. Do not select based on unsupported “fastest” claims. Playwright, Selenium, Puppeteer, and Cypress document their respective capabilities and constraints.
Choose by browser target
- Chromium only: Any of the four may fit, depending on language and test workflow.
- Firefox: Playwright, Selenium, Puppeteer, and Cypress document Firefox options; check the exact browser versions and execution environment you need.
- WebKit engine: Playwright includes WebKit support. Cypress labels its WebKit support experimental and documents known limitations.
- Branded Chrome or Edge: Playwright documents using branded Chrome and Edge. If a specific branded browser matters, verify the framework’s configuration and CI availability.
- Actual Safari: Playwright states that its WebKit is not branded Safari and notes platform-dependent differences. Test on the target Safari environment when Safari itself is the requirement.
- Remote machines and operating systems: Selenium Grid is explicitly designed to route WebDriver scripts to remote browser instances and distribute execution. Playwright documents worker parallelism and sharding, which are a different operating model.
Choose by language and task
Prefer the tool that fits the runtime already used for your application, CI, and maintenance. Playwright documents TypeScript, Python, .NET, and Java bindings. Puppeteer is a JavaScript library. Selenium provides language-specific bindings. Cypress is commonly evaluated as a test framework; confirm current language and runner requirements in its documentation.
- Repeatable end-to-end tests: Start by evaluating Playwright Test or Cypress. Consider Selenium if WebDriver interoperability or an established Selenium suite is important.
- One-off web administration or scripting: Selenium explicitly describes web administration as an automation use. Playwright and Puppeteer can also drive standalone scripts.
- Browser screenshots or PDFs: Puppeteer documents both; Playwright can be used for browser scripting and tests. If the task is simply to capture a URL as an image or PDF, a screenshot API can avoid managing a browser process.
- Agent-driven browser workflow: Playwright documents workflows for AI agents. For agents that need page screenshots or PDFs rather than interactive browser control, ScreenshotNeo also provides an MCP server described below.
Runnable starting point: Playwright Test with TypeScript
This small test opens a page, checks a visible heading, and saves a screenshot on success. Install the test package, install the Chromium browser binary, then run the test.
npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium
Create tests/home.spec.ts:
import { test, expect } from '@playwright/test';
test('home page has the expected heading', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page.getByRole('heading', { name: /playwright/i }).first()).toBeVisible();
await page.screenshot({ path: 'artifacts/home.png', fullPage: true });
});
Run it with npx playwright test. For multiple engines, install the required browser binaries and configure projects or use the CLI’s project selection. The official Playwright getting-started guide covers setup; the browser documentation explains browser installation and branded browser options.
Useful Playwright configuration choices
- Projects: Define a project per browser or device profile so the same test suite runs against each target.
- Workers: Parallel workers can shorten suite wall time, but tests that modify shared accounts or data need isolation. Reduce worker count when the CI machine or application cannot handle concurrent load.
- Retries and traces: Enable traces for failures to inspect actions and page state. Treat retries as a diagnostic aid, not a substitute for fixing nondeterministic tests.
- Waiting: Prefer locator actions and web-first assertions that wait for the expected condition over fixed sleeps. Use a deliberate wait for a known application state when necessary.
- Browser identity: Select the browser engine that matches the question being tested. A WebKit engine check does not establish identical behavior in every branded Safari release.
See the official docs for test configuration, parallel execution, and trace debugging.
Other runnable entry points
Selenium with Python
Install Selenium, then run a basic browser session. Selenium Manager can configure drivers for supported local setups; consult the current installation documentation for prerequisites.
python -m pip install selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument('--headless=new')
driver = webdriver.Chrome(options=options)
try:
driver.get('https://www.selenium.dev/')
heading = WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((By.TAG_NAME, 'h1'))
)
print(heading.text)
driver.save_screenshot('selenium-home.png')
finally:
driver.quit()
Use explicit waits for the condition your task depends on. For remote execution, configure a RemoteWebDriver against a protected Selenium Grid endpoint and specify the desired capabilities/options. Selenium’s official documentation covers bindings, browser setup, and Grid.
Puppeteer with Node.js
Install Puppeteer and save the following as capture.mjs. The package’s installation behavior and browser setup are described in its official documentation.
npm install puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'example.png', fullPage: true });
await page.pdf({ path: 'example.pdf', format: 'A4', printBackground: true });
} finally {
await browser.close();
}
For dynamic pages, choose a readiness condition based on the site. Network-idle conditions may not settle on pages with long-lived requests; waiting for a relevant selector can be more reliable. See Puppeteer installation, page interactions, and screenshots.
Cypress test example
Install Cypress and open its runner to scaffold or run a test. The exact setup can vary with the existing project; follow Cypress’s current installation guide.
npm install --save-dev cypress
npx cypress open
A basic E2E test can use Cypress commands and retryable assertions:
describe('home page', () => {
it('shows the page heading', () => {
cy.visit('https://example.com');
cy.get('h1').should('be.visible');
});
});
Check the installation guide and browser support documentation, especially before relying on experimental WebKit behavior.
When a screenshot API is a better fit
Browser frameworks are useful when a task must interact with a page, assert behavior, submit forms, or reproduce a user journey. For the narrower job of getting an image or PDF of a URL, running a browser means owning browser installation, page readiness, and capture code. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media: one GET request can return PNG, JPEG, WebP, or PDF. Its API accepts the parameter names used by other screenshot APIs, which can make switching simpler.
ScreenshotNeo is the first screenshot API to try here because it removes cookie banners, popups, and chat widgets before capture, bills only clean shots, and its paid plans start at $5 for 3,000 shots.
ScreenshotNeo examples
Get an API key, then use one of these complete request examples. See the ScreenshotNeo API documentation for parameters and response details.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Relevant capture options
ScreenshotNeo has options for full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or a custom viewport, and retina scale. It can return PDFs with paper size, margins, landscape orientation, and page ranges; it can also render HTML/CSS to an image. For page-specific preparation, set custom CSS or JavaScript, click an element, hide selectors, or wait for a selector, a delay, or network idle.
Network controls include blocking ads, trackers, requests, or resource types. You can provide custom headers, cookies, user agent, and Authorization, plus timezone and geolocation. Other options include transparent background, image resizing, caching with a chosen TTL, signed links for public <img> tags, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI spec. Cookie/consent handling can be turned off step by step if the page requires the original state.
Or skip the browser setup
Make one request to capture a URL. The examples and full option list are in the ScreenshotNeo docs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
- Cookie banners are accepted as a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot. Each cleanup step can be turned off.
- Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers state the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and any MCP client. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; all features are available on every plan. Yearly billing gives two months free.
Sign up free for 1,000 screenshots a month, no card required.
Reliability, performance, and cost
Make browser runs reliable
- Wait for a user-visible condition or a stable selector instead of sleeping an arbitrary duration.
- Keep tests independent: isolate browser contexts, test data, accounts, and cleanup so parallel workers do not race over shared state.
- Capture traces, screenshots, and relevant logs when failures occur. A retry can reveal flakiness, but repeated retries can also hide a real defect.
- Test the actual browser target. Engine coverage is useful, but branded browser and operating-system differences can matter.
- For remote browser infrastructure, protect Grid endpoints from external access and budget time to operate nodes, browser versions, and capacity.
Plan for speed and operating cost
There is no comparable benchmark in the supplied evidence, so performance should be measured on your own pages and infrastructure. Browser runs consume local or hosted CPU and memory, and parallelism trades wall-clock time for concurrent resource use. Selenium Grid distributes work across machines but requires infrastructure. For URL-only screenshot jobs, an API request can reduce browser installation and maintenance work; compare the service price with the engineering and compute cost of running browsers yourself.
ScreenshotNeo pricing is Free for 1,000 shots per month with no card; Starter is $5 for 3,000; Growth $15 for 15,000; Pro $39 for 60,000; Scale $99 for 250,000; and Business $249 for 1,000,000. Yearly billing gives two months free. Each response indicates the page verdict and whether it was billed; only clean shots are billed.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Element lookup fails immediately | The page has not rendered the element, the selector is wrong, or it is in a frame/shadow root. | Use a locator or explicit condition wait, verify the selector against the live DOM, and handle frames or shadow DOM using the framework’s supported APIs. |
| Test passes locally but fails in CI | Different browser versions, slower resources, missing system dependencies, or shared test state. | Install the required browser and dependencies, pin the framework version, wait on page state, and isolate test data. Preserve traces or screenshots from the CI failure. |
| Click is intercepted or times out | An overlay, animation, consent dialog, or disabled control covers the target. | Assert the intended overlay state, dismiss it when appropriate, and wait for the target to be visible and actionable. Avoid forcing a click until you understand why normal interaction is blocked. |
| Network-idle wait never completes | Analytics, polling, streaming, or another long-lived request keeps the network active. | Wait for a page-specific selector or application-ready signal instead of global network silence. |
| Screenshot is blank or content is missing | Capture happened before rendering, lazy content was not loaded, or a bot check blocked the page. | Wait for the content selector, scroll or enable lazy-image handling, and inspect the page response and browser logs. For an API response, inspect its page-verdict and billing headers. |
| Browser or driver cannot launch | Browser binary, driver, OS dependency, or executable path is missing or incompatible. | Use the framework’s browser installation instructions, check CI system dependencies, and align installed browser and driver versions. Selenium Manager may handle local driver setup in supported environments. |
| Cypress WebKit behavior differs or is unstable | WebKit support is experimental and has documented limitations. | Consult current Cypress known issues and use a suitable supported browser for required production coverage. |
| Screenshot API returns an error or unexpected result | Invalid key/URL, target timeout, unsupported page behavior, or a non-clean page verdict. | Check the request parameters and HTTP status, inspect X-Page-Verdict and X-Billed, and adjust wait, viewport, or page preparation options as documented. |
FAQ
Is Playwright always the best choice?
No. It is a reasonable first evaluation for a new cross-browser project, but team language, branded browser needs, existing infrastructure, and test workflow can change the choice.
Can Puppeteer test Firefox?
Yes. Puppeteer’s documentation describes Chrome and Firefox support; its documented browser set does not include WebKit.
Does Playwright WebKit mean Safari is covered?
It provides WebKit engine coverage, but Playwright says it is not branded Safari and notes platform-dependent differences. Validate against Safari when that exact browser is a requirement.
Should I use a framework or a screenshot API?
Use a framework for interaction, assertions, and multi-step browser workflows. Use a screenshot API when the required output is a URL screenshot or PDF and you do not need to control a browser session.
Can an AI agent capture a page with ScreenshotNeo?
Yes. Its MCP server exposes screenshot, page-info, and PDF capture tools to Claude, Cursor, and any MCP client.
