Your Playwright project already knows how to drive Chromium. Point its CDP socket at wss://connect.steel.dev?apiKey=... and the same code runs inside Steel's managed browsers with the evidence, proxies, and observability you could not get from local Chrome.
Use the one-line swap when Steel's defaults cover you. When you need proxies, CAPTCHA solving, or named sessions for auditing, create the session through the Steel SDK first, then attach Playwright to that session over CDP and release it when you are done. Either path keeps your existing tests, fixtures, and reporters unchanged.
What stays the same
- Playwright's test runner, assertions, fixtures, and reporters.
- Page objects, selectors, and the
pageAPI surface. - Existing CI steps, including environment variables and secrets workflows.
- Local development loop: you can still run Playwright locally; you only swap the launch line when you want Steel's fleet.
What Steel adds
| Job | Without Steel | With Steel |
|---|---|---|
| Session startup | Local Chrome plus warmup scripts that often take seconds | Sub-second Steel Cloud sessions that are ready on connect (chromium.connectOverCDP) |
| Long runs | Local browser dies or loses cookies at every CI run | 24-hour managed sessions that preserve auth context while you control resets |
| Anti-bot handling | DIY proxies, fingerprints, CAPTCHA solves | Managed proxies, stealth, and CAPTCHA solving when you create the session via API |
| Evidence | Sparse console logs | Live viewer, HLS replays, and retrieved sessions when you pass your own sessionId |
| Scale | Steel Local or DIY infra tops out around one session | Steel Cloud handles hundreds of concurrent sessions per plan and exposes Files, Credentials, and other APIs when you grow |
Minimal integration path
Method 1: One-line change (default settings)
- Keep your existing script.
- Swap
chromium.launch()(or.launch()on another browser) forchromium.connectOverCDP('wss://connect.steel.dev?apiKey=MY_STEEL_API_KEY'). - Run your suite; Steel starts, records, and releases the session automatically when Playwright closes the browser or the process ends.
- Optional: append
&sessionId=<uuid>so you can query or release the run later via the Steel API.
Method 2: Create, connect, release (custom features)
- Use the Steel SDK (
steel-sdkfor Node,steelfor Python) to callclient.sessions.create({ useProxy: true, solveCaptcha: true, ... }). - Connect Playwright with
chromium.connectOverCDP('wss://connect.steel.dev?apiKey=...&sessionId='+session.id)so you enter the same browser instance Steel created. - Reuse the provided context (
browser.contexts()[0]orbrowser.contexts[0]) instead of callingbrowser.newContext()so the run stays in the recorded session. - When finished, close the browser and call
client.sessions.release(session.id)so the slot frees immediately rather than timing out.
Example code
Node / TypeScript
import Steel from 'steel-sdk';
import { chromium } from 'playwright';
import { config } from 'dotenv';
config();
const client = new Steel({ steelAPIKey: process.env.STEEL_API_KEY });
async function run() {
const session = await client.sessions.create({ useProxy: true, solveCaptcha: true });
const browser = await chromium.connectOverCDP(
`wss://connect.steel.dev?apiKey=${process.env.STEEL_API_KEY}&sessionId=${session.id}`
);
const page = browser.contexts()[0].pages()[0] ?? (await browser.contexts()[0].newPage());
await page.goto('https://news.ycombinator.com');
await browser.close();
await client.sessions.release(session.id);
}
run().catch((err) => {
console.error(err);
process.exit(1);
});Python
import os
from dotenv import load_dotenv
from playwright.sync_api import sync_playwright
from steel import Steel
()
client =(steel_api_key=.("STEEL_API_KEY"))
def run():
session = client.sessions.(use_proxy=True, solve_captcha=True)
playwright =().()
browser = playwright.chromium.(
f"wss://connect.steel.dev?apiKey={.('STEEL_API_KEY')}&sessionId={.}"
)
context = browser.[0]
page = context.[0] if context.pages else context.()
page.("https://news.ycombinator.com")
browser.()
client.sessions.(.)
playwright.()
if __name__ == "__main__":
()Trade-offs and guardrails
- Method 1 always uses Steel's defaults: no proxy, standard fingerprint, CAPTCHA solving off. Use Method 2 whenever the target site needs more stealth.
- Sessions stay billable until you release them. Always call
sessions.release()even if your Playwright test failed midway. - Stick to the first context returned by Steel. Spinning up a new context launches a second, untracked browser without replays.
- Steel Local is ideal for development and handles roughly one session at a time. Use Steel Cloud for multi-session CI runs or when you need managed proxies and CAPTCHA solving.
- Long workflows still need checkpoints: Steel sessions can run up to 24 hours, but you should checkpoint credentials and files via the APIs if the workflow must resume days later.
When this fits (and when it does not)
- Works when you already have Playwright suites and want more reliable browsers, evidence, or anti-bot tooling without touching your test logic.
- Works when you need to pair browser runs with Credentials, Files, or human-in-the-loop approvals later; the same
sessionIdlinks every artifact. - Not a fit if you only need a one-off static scrape or a single screenshot; Quick Actions or Steel's scraping endpoints are faster for that path.
Next steps
- Drop Method 1 into a single spec to validate recordings in the Steel live viewer.
- Once the swap sticks, upgrade to Method 2 in the specs that need proxies, CAPTCHA solving, or explicit naming, then track those session IDs in your CI logs.
- Clone the Hacker News starter (
steel-playwright-starterorsteel-playwright-python-starter) to copy the full error-handling pattern into your repo.
Humans use Chrome. Agents use Steel.