Can an AI agent use Steel (steel.dev)?
As of 12 Oct 2026, an AI agent gets into Steel on its own and does 100% of the core work.
- Find: Docs for agents: llms.txt, an OpenAPI and an MCP server; a docs page for each core task.
- Access: Pay per call, sign-up in a browser.
- Use: 100% of the work.
- Pay: Pays per call for part of it: Paid $0.01 a call (x402), 12 Oct.
- Leave: Nothing to cancel: Deletes its data, but the login stays.
Ways in: Pay per call, 1 of 8 core tasks · Sign-up in a browser, 8 of 8 core tasks
To reach L5 · Autonomous: let an agent buy credits or a plan without a person.
What the levels meanDriven 12 Oct 2026Same level in 2 drives in a rowRules of 15 Oct 2026Claim this page
Written by our agents from this drive. How we write.
Who gets in
- Agents on a serverGet in on their own.
- Signed agentsThe sign-up page opens.
- Agents in a person's browserNot measured yet.
Sign-up page checked 15 Oct 2026. Signed agents: the ones Cloudflare verifies. Where agents come from
What an agent can do in Steel (steel.dev).
On its own
- Start a cloud browser session, click around and browse pages, then shut it down
- Grab a page as a screenshot, a PDF, or clean text with one call
- Save a login and have it auto-filled and submitted next time
5 more
- Keep a browser's cookies and sign-in state saved for reuse later
- Upload and manage files or browser extensions used during a session
- Watch a session live or review a recording and action log afterward
- Do all of the above through a command-line tool or the official AI-agent (MCP) connector
- Pay a tiny per-use fee automatically with no account at all, for basic scraping/screenshots/PDFs
What it can't, and why
- Sign up and get its first API key without a browsersign-up and key creation only happen in the web dashboard
- Use CAPTCHA-solving or proxy features on the free trialthose require adding at least $10 of paid balance by card
- Delete its account so the login itself stops workingdeleting the organization wipes its data but the login still gets a fresh empty one
1 more
- Revoke its own API key or delete a saved browser profile by itselfboth actions are dashboard-only; no API exists for them
Can an AI agent sign up for Steel on its own?
Yes, in a browser, without a human. Continue with Email on app.steel.dev (no password, a sign-up link by mail), then a 3-step questionnaire; no bot check seen. No sign-up API.
Every way in it offers, as our agent tried them
| Way in | What happened |
|---|---|
| Pay per call | Got in, under a minute |
| Stripe Projects | Not tried: no account yet |
| CLI code login | Not reached |
| Company single sign‑on | Not tried: needs a person's account |
Does Steel (steel.dev) have an MCP server, and can an agent connect to it?
Yes. Steel has an official MCP server. The server runs on the agent's own machine, lists 16 tools and speaks the 28 Jul 2026 MCP spec.
A new agent can get in on its own: in the drive of 12 Oct 2026, our agent signed up, verified and got its key without a person. Through the MCP server, 7 of our agent's 7 attempts worked.
What the drive found
APIOpenAPIMCP · officialCLISDKllms.txt–Webhooks–SandboxHow does an AI agent get a Steel (steel.dev) API key?
In the drive of 12 Oct 2026, our agent got its Steel API key in a browser, without a person. API keys are made in the dashboard. POST /v1/api-keys mints project keys headless, but only with an organization key, which is itself dashboard-only; revoke is dashboard-only.
Can an AI agent delete its Steel account?
Not all of it: it deletes its data, but the login stays. Delete organization (hold to delete) removed the org's sessions, profiles, credentials and keys: the project key and org key then answered 401. But the sign-in login (the agent's inbox / the GitHub-linked identity) kept working: it was dropped straight into a freshly created empty org ("Anon's Team"), and the GitHub grant had to be revoked separately on GitHub's side. There is no self-serve user-account delete (Settings > Security is "Page Not Found"); account termination is by request only per the privacy policy. Deleted: org data and keys. Stayed: the user's login.
Can an AI agent pay for Steel?
Pays per call for part of it: paid $0.01 a call (x402), 12 Oct.
Reached the card form and stopped: Scale, $250 a month.
Checkout: a Stripe card formAn invisible bot check loaded at checkoutWallets: Google Pay, another walletPaid on its own: $0.01 in USDC through x402 on Base, 12 Oct 2026. 0xdd2f191a…b03b20
- Stripe Projects on its own domain
- x402 on its own domain · Base, Solana
It has a free plan.
What would let agents pay on their own
- Sell the plan or credits over the rail, not only side endpoints. Let the paid call buy what the product sells, so the rail covers the whole product.
We paid one x402 v2 call ($0.01 USDC on Base, tx 0xdd2f191a...4b03b20, confirmed, 200 with content): pay-per-call works with no account, but only for scrape, screenshot and PDF. Plans (Scale $250/month) and credit top-ups (from $10) go through a Stripe card form in the dashboard; Stripe Projects is documented but not tried.
Ways to pay checked 12 Oct 2026.
For the team behind Steel.
Where agents stop in Steel (steel.dev) today, and the fixes that get them further, highest impact first.
The fixes, highest impact first
- Let an agent sign up and get a key headlessStep-by-step guide
- Let x402 or Stripe Projects fund the wallet
- Add DELETE for profiles and API keys
4 more fixes
- Let users delete their own account
- Make the auth-context round trip lossless
- Return 409 on duplicate creates, not 5xx
- Update the x402 and forge docs
- Claim it free and reply next to the result.
- A Drive, $49: your private report, with where agents stop, the evidence and the fixes that move your level.
- Fixed something? Order a Drive ($49) any time, or the Monthly drive ($29/month).
Details
Your API, MCP server or CLI is the product? An Audit ($299) also checks every documented route, MCP tool and CLI command against your docs. Compare plans.
Already claimed it? Open it in your dashboard.
Rather not be driven? Block AgentDrivesAgent in robots.txt or write to us. We stop driving it ourselves; this page stays, with its date.
Embed the badge on your site
Behind the level.
The part scores and the measures behind them.
Part scores and the measures · the docs · level history · paid plans
Part scores
- Holds up: 84 of 100, fair
Agent-ready setup
- llms.txt: found
- OpenAPI: found
- MCP server: found
- CLI: found
Not measured in this drive: Docs match (part of an Audit).
The measures
- Work done100
- Reliability79
- Crash test89
The docs
211 of 218 pages read. Complete.
- A getting-started page
- An auth page
- Errors listed with their codes
- Code examples on most pages
- Newest page updated 12 Oct 2026
- A page for 10 of 10 core tasks
Read on 12 Oct 2026.
Tried as documented
15 of 19 documented ways to do the core work worked when tried.
| What happened | Ways |
|---|---|
| Worked when tried | 15 |
| Documented, didn't work | 0 |
| Ran, but not read back | 1 |
| Not tried (a paid plan, or no way to reach it) | 3 |
| Worked, but not in the docs | 2 |
Level history
- 5 Oct 2026L4Runs on its own
- 12 Oct 2026new driveL5Autonomous
- 13 Oct 2026new rulesL4Runs on its own
Needs a paid plan
Not counted in the level. Our drive used free plan (Launch, $30 one-time credits).
| Capability | Plan |
|---|---|
| Auto-solve CAPTCHAs | Launch with $10+ paid balance, or Scale |
| Residential proxies with geo targeting | Launch with $10+ paid balance, or Scale |
| Dedicated static IP | Scale ($5/IP) |
How Steel handles an agent's mistakes · 10 probes · core run 3×
Malformed requests, a retry, a wrong key and a short burst of reads on our agent's own account, and how Steel answered.
- Clear errors79
- Recovery89
- Safe retries100
400/401/404 answers name the fix and link the docs; duplicate creates answer 500 or 504 instead of 409 (no duplicates made), and a missing route answers 421 text/plain. 15 quick reads all passed (limit 60/min).
- Vaguerepeat a createStore and auto-inject login credentials (incl. TOTP) into a session201 then 500 {"message": "Steel hit an unexpected server error while processing this request. Retry shortly; if it persists, contact support with the request details.", "error": "Internal Server Error
- Vaguerepeat create with the same custom sessionIdCreate a cloud browser session201, then 504 "error code: 504" (text/plain, after a long wait); the session itself was not duplicated
- Clearmissing required urlScrape a page to HTML/markdown without managing a session400 {"message": "url must be a valid URL including the scheme, e.g. https://example.com.", "error": "Bad Request"}
- Clearbad enum valueCreate a cloud browser session400 {"message": "device is not one of the supported values. Use one of the documented values and retry.", "error": "Bad Request"}
- Clearunknown id in own namespaceCreate a cloud browser session404 {"message": "Session not found. It may have ended, expired, or belong to a different organization. Check the session id, or list your sessions to confirm it exists.", "error": "Not Found", "linkTo
- Clearrelease unknown sessionRelease/stop a browser session404 {"message": "Session not found"}
- Clearwrong API keyCreate a cloud browser session401 {"error": "Unauthorized", "message": "Invalid Steel API key. The key sent in the 'steel-api-key' header does not match any account. Create or rotate a key at https://app.steel.dev/settings/api-key
- Clearburst of 15 GET /v1/sessionsCreate a cloud browser session15 x 200; no rate-limit headers on GET (X-RateLimit-Limit 60 / Remaining / Reset seen earlier on a POST /v1/sessions 403); plan limit 60 requests per minute
- Clearinterrupt a persistProfile session right after createPersist and reuse a browser profile (cookies, auth, extensions, settings) across sessionssession released; its profile ec9e9236 still reaches READY (consistent, no half state)
- Clearrevoked keyCreate a cloud browser session401 {"error": "Unauthorized", "message": "Invalid Steel API key. The key sent in the 'steel-api-key' header does not match any account. Create or rotate a key at https://app.steel.dev/settings/api-key
For agents and builders.
The drive as data, every checkpoint and all 110 attempts.
How an agent connects · 7 tips
/api/tools/steel.json/tools/steel.mdMCP get_tool {"slug":"steel"}
- Send the key in the steel-api-key header, not Authorization: Bearer
- Retry GET /v1/sessions/{id} once if it 404s right after create
- Use --json on every CLI command; forge templates are playwright-ts, playwright-py, ...
- Profile PATCH must be multipart/form-data
- When reusing GET /context, rewrite localStorage keys to https://origin
- The MCP server refuses a fresh session when saved profiles exist: pass guest=true or a profile_id
- The dashboard shows a new key once; copy it in the same step
Every checkpoint, in detail · 9 checkpoints
- DocsHeadlessllms.txt with agent instructions, every page as .md and via Accept: text/markdown, OpenAPI 3.0.3 found through /.well-known/api-catalog.
- Sign upIn a browserContinue with Email on app.steel.dev (no password, a sign-up link by mail), then a 3-step questionnaire; no bot check seen. No sign-up API.
- VerifyIn a browserThe sign-up link arrived in under a minute and was opened in the browser; it lasts 10 minutes.
- KeyIn a browserAPI keys are made in the dashboard. POST /v1/api-keys mints project keys headless, but only with an organization key, which is itself dashboard-only; revoke is dashboard-only.
- First callHeadlessGET /v1/sessions with the steel-api-key header -> 200.
- First actionHeadlessCreated a cloud browser session with the Node SDK, drove it over CDP with Playwright and released it.
- PayHeadlessWe paid one x402 v2 call ($0.01 USDC on Base, tx 0xdd2f191a...4b03b20, confirmed, 200 with content): pay-per-call works with no account, but only for scrape, screenshot and PDF. Plans (Scale $250/month) and credit top-ups (from $10) go through a Stripe card form in the dashboard; Stripe Projects is documented but not tried.
- CancelNot neededNothing to cancel: our agent used it pay-per-call.
- DeleteBlockedDelete organization (hold to delete) removed the org's sessions, profiles, credentials and keys: the project key and org key then answered 401. But the sign-in login (the agent's inbox / the GitHub-linked identity) kept working: it was dropped straight into a freshly created empty org ("Anon's Team"), and the GitHub grant had to be revoked separately on GitHub's side. There is no self-serve user-account delete (Settings > Security is "Page Not Found"); account termination is by request only per the privacy policy. Deleted: org data and keys. Stayed: the user's login.
The attempt log · 110 attempts
| Capability | Route | Result | Evidence |
|---|---|---|---|
| Docs: Read llms.txt, auth, CLI, x402 docs | api | done ✓ | docs.steel.dev/llms.txt 200 (29 KB, Agent Instructions section); /overview/authentication.md, /integrations/x402.md, /overview/steel-cli.md all 200 text/markdown |
| OpenAPI: Find OpenAPI via api-catalog | api | done ✓ | docs.steel.dev/.well-known/api-catalog (RFC 9727 linkset) -> https://api.steel.dev/sdk-openapi.json, openapi 3.0.3, 75 operations incl. POST /v1/api-keys |
| Markdown docs: Accept: text/markdown and .md URL | api | done ✓ | GET docs page with Accept: text/markdown -> 200 text/markdown, Vary lacks Accept (rsc, next-router-*); .md URL -> 200 text/markdown |
| Well-known cards: ai-catalog, agent-card, agent.json, mcp server-card, oauth PRM/AS, ucp, auth.md, SKILL.md on steel.dev and docs.steel.dev | api | done ✓ | all 404 text/html on steel.dev and docs.steel.dev; api.steel.dev answers 421 for those paths |
| robots/aipref: robots.txt Content-Usage and Content-Usage header | api | done ✓ | steel.dev/robots.txt: "User-Agent: * / Allow: /" + Sitemap; no Content-Usage or Content-signal lines; no Content-Usage response header |
| MCP: Find MCP server | api | done ✓ | github.com/steel-dev/steel-mcp-server v3.0.0 (official, stdio via npx github:steel-dev/steel-mcp-server; README: "mcp.steel.dev is not live yet"); mcp.steel.dev no answer (000) |
| API auth error: Unauthenticated GET /v1/sessions | api | done ✓ | 401 application/json {"error":"Unauthorized","message":"Missing API key. Authenticate by sending your Steel API key in the 'steel-api-key' header (Steel does not use 'Authorization: Bearer'). Create or rotate a key at https://app.steel.dev/settings/api-keys."} |
| Sign up: Sign up at app.steel.dev with Continue with Email | browser | done ✓ | app.steel.dev/sign-up -> sign-in page with GitHub/Google/Email; email the agent's inbox accepted, "Email Sent. Check your Inbox"; no CAPTCHA seen (Clerk at clerk.steel.dev) |
| Verify email: Open the sign-up link from the mail | browser | done ✓ | mail "Your sign up link" from notifications@steel.dev arrived in <1 min; link (clerk.steel.dev/v1/verify, 10 min expiry) opened in browser -> /questionnaire -> /quickstart, wallet shows $30.00 |
| API key: Create API key in dashboard | browser | done ✓ | quickstart "Generate API Key" made an Onboarding key; Settings > API Keys > Create API key (project key "agentdrives-main") shown once |
| First API call: GET /v1/sessions with the key | api | done ✓ | 200 {"sessions":[],"nextCursor":null} |
| Create a cloud browser session: client.sessions.create then retrieve | sdk | done ✓ | session 26630c5f-... created (SDK 0.18.0); GET /v1/sessions lists it status live |
| Create a cloud browser session: retrieve right after create | sdk | failed | GET /v1/sessions/26630c5f-... immediately after create -> 404 "Session not found. It may have ended, expired, or belong to a different organization..."; the list a minute later shows it live |
| Drive the session over CDP: Playwright connectOverCDP, goto example.com, click link | api | failed | not reached: the script stopped on the 404 read before connecting (first attempt) |
| Release a session: release the session left live by the failed run | api | done ✓ | POST /v1/sessions/26630c5f-.../release -> 200; GET -> status released |
| Create a cloud browser session: SDK create, retrieve, Playwright CDP drive, release | sdk | done ✓ | retry after the failed first run: session 81a95273 created in 840 ms, status live, websocketUrl set |
| Create a cloud browser session: SDK create, retrieve, Playwright CDP drive, release | sdk | done ✓ | GET right after create: status live, websocketUrl present |
| Drive the session over CDP: SDK create, retrieve, Playwright CDP drive, release | sdk | done ✓ | page.goto example.com -> url https://example.com/ title "Example Domain"; clicked link -> https://www.iana.org/help/example-domains "Example Domains" |
| Release a session: SDK create, retrieve, Playwright CDP drive, release | sdk | done ✓ | sessions.release -> success true; GET -> status released |
| First action: Create a browser session and drive it | sdk | done ✓ | session 81a95273 created live, driven to example.com and released (see ..03) |
| : SDK session chain | sdk | done ✓ | session a85d34cb live in 984 ms |
| : SDK session chain | sdk | done ✓ | GET right after create: live |
| : SDK session chain | sdk | done ✓ | goto example.com, click -> iana.org/help/example-domains |
| : SDK session chain | sdk | done ✓ | release success; GET status released |
| Create a cloud browser session: update a session | api | skipped | OpenAPI has no update operation for sessions (create, get, list, release, release-all only) |
| Scrape a page: POST /v1/scrape example.com markdown+html | api | done ✓ | 200; content.html has <title>Example Domain</title>; content.markdown starts "This domain is for use in documentation examples..."; metadata.statusCode 200 |
| Screenshot: POST /v1/screenshot example.com fullPage | api | done ✓ | 200 {"url":"https://images.steel.dev/v1/static/....png"}; download 200 image/png, 125 KB, 1911x988, shows the example.com text |
| PDF: POST /v1/pdf example.com | api | done ✓ | 200 {"url":"https://files.steel.dev/v1/static/....pdf"}; download 200 application/pdf, %PDF-1.4, 101 KB |
| Scrape a page: scrape https://www.iana.org/help/example-domains | api | done ✓ | 200; markdown 851 chars; title Example Domains |
| Screenshot: screenshot https://www.iana.org/help/example-domains | api | done ✓ | 200; download 200 196693 |
| PDF: pdf https://www.iana.org/help/example-domains | api | done ✓ | 200; download 200 124748 application/pdf |
| Scrape a page: scrape https://example.org | api | done ✓ | 200; markdown 957 chars; title Example Domain |
| Screenshot: screenshot https://example.org | api | done ✓ | 200; download 200 125077 |
| PDF: pdf https://example.org | api | done ✓ | 200; download 200 101231 application/pdf |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | POST /v1/credentials 201 {namespace ad-t1, origin https://agentdrives.com, label ...} (value not echoed) |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | GET /v1/credentials?namespace=ad-t1 200 lists it, no secret values returned |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | PUT /v1/credentials label -> "AgentDrives test login v2", 200, updatedAt changed; list shows v2 |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | session namespace ad-t1 credentials {autoSubmit:true}: goto https://agentdrives.com/login -> form filled and submitted by Steel, our route got POST with email+password, page ended on /dashboard "Signed in" |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | DELETE /v1/credentials {origin, namespace} -> 200; list -> {'credentials': []} |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | done ✓ | trial 2: create 201; session ended on https://agentdrives.com/dashboard dash=True; delete 200; list after {'credentials': []} |
| Store and auto-inject credentials: store a login for https://agentdrives.com (our own origin, page served by our script), inject into a session | api | failed | trial 3: create 500; session ended on https://agentdrives.com/dashboard dash=True; delete 200; list after {'credentials': []} |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | done ✓ | session persistProfile:true -> profileId e6282f95; cookie ad_session + localStorage set on https://agentdrives.com; after release profile status READY |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | done ✓ | GET /v1/profiles/{id} 200 status READY, sourceSessionId, userAgent; GET /v1/profiles lists it |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | done ✓ | PATCH /v1/profiles/{id} JSON {userAgent} -> 400 "Profile updates must be sent as multipart/form-data..."; same field as multipart -> 200, GET shows the new userAgent |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | done ✓ | new session with profileId e6282f95: cookie ad_session=v1be694a1 and localStorage ad_ls=v1be694a1 present without setting them again |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | failed | DELETE /v1/profiles/{id} -> 421 "No route defined for DELETE /v1/profiles/..."; OpenAPI has no profile delete; docs: "If a profile is not used after 30 days, it will be automatically deleted"; profile still listed |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | done ✓ | trial 2: profile 0fc4941e status READY; new session saw cookies ['ad_session=v483c45cd'] ls v483c45cd |
| Persist and reuse a browser profile: persist a profile with a cookie+localStorage on our own origin, reuse it in a new session | api | failed | trial 3: profile 94c4028f status READY; new session saw cookies [] ls vcbfd2eb7 |
| Auto-solve CAPTCHAs: POST /v1/sessions {solveCaptcha:true} | api | plan_gated | 403 {"message":"Launch requires at least $10 in paid balance to use CAPTCHA solving or Steel proxies. Add credits, bring your own proxy via 'proxyUrl', or upgrade to Scale.","error":"Forbidden","linkToDocs":"https://docs.steel.dev/overview/pricinglimits"} |
| Residential proxies with geo targeting: session useProxy {geolocation:{country:'US'}}, check egress IP | sdk | plan_gated | 403 {"message":"Launch requires at least $10 in paid balance to use CAPTCHA solving or Steel proxies. Add credits, bring your own proxy via 'proxyUrl', or upgrade to Scale.","error":"Forbidden","linkToDocs":"https://docs.steel.dev/overview/pricinglimits"}; baseline session without proxy egress IP 216.246.40.62 (api.ipify.org) |
| Stealth mode: POST /v1/sessions {stealthConfig:{humanizeInteractions:true}} | api | partial | 201 session 942e55c7 live; released 200 |
| Scoped key headless: POST /v1/api-keys with the project key, then with an organization key | api | done ✓ | project key -> 403 {"message":"API keys cannot manage API keys"}; organization key (made once in the dashboard) + projectId -> 201 {id, key ste-••••, scopeType project, projectId} |
| Scoped key headless: GET /v1/sessions with the new project key | api | done ✓ | 200 |
| Docs claim: Scrape a page to HTML/markdown without managing a session: steel scrape <url> | cli | done ✓ | steel 0.4.4 --json: data.content.markdown has the example.com text |
| Docs claim: Capture a screenshot of a page: steel screenshot <url> --full-page | cli | done ✓ | success true, data.url images.steel.dev/...png |
| Docs claim: Generate a PDF of a page: steel pdf <url> | cli | done ✓ | success true, data.url files.steel.dev/...pdf |
| Docs claim: Create a cloud browser session: steel browser start | cli | done ✓ | session 54f0d6bb live, named ad-cli; steel browser sessions lists it |
| Docs claim: Drive the session with Playwright/Puppeteer/Selenium over CDP: steel browser open/click/fill/type | cli | done ✓ | open https://example.com -> title Example Domain; snapshot -i -> link "Learn more" [ref=e1] |
| Docs claim: Release/stop a browser session: steel browser stop | cli | done ✓ | stoppedSessions ["ad-cli"] |
| Docs claim: Persist and reuse a browser profile (cookies, auth, extensions, settings) across sessions: steel profile import/sync/list/delete | cli | partial | profile list returns [] while GET /v1/profiles lists 3 API profiles; the CLI manages named profiles imported from a local browser, and delete takes a name, so API profiles cannot be deleted from the CLI |
| Docs claim: Store and auto-inject login credentials (incl. TOTP) into a session: steel credentials create/list/update/delete | cli | done ✓ | credentials list --json -> {"data":[],"success":true} (matches API after our deletes) |
| Docs claim: Auto-solve CAPTCHAs encountered during a session: steel browser captcha solve | cli | plan_gated | solving needs $10 paid balance (API 403 on solveCaptcha) |
| Docs claim: Create a cloud browser session: POST /v1/sessions | api | done ✓ | 201 live |
| Docs claim: Drive the session with Playwright/Puppeteer/Selenium over CDP: wss://connect.steel.dev?apiKey=&sessionId= | api | done ✓ | Playwright connectOverCDP drove example.com |
| Docs claim: Release/stop a browser session: POST /v1/sessions/{id}/release | api | done ✓ | status released |
| Docs claim: Scrape a page to HTML/markdown without managing a session: POST /v1/scrape | api | done ✓ | 200 html+markdown |
| Docs claim: Capture a screenshot of a page: POST /v1/screenshot | api | done ✓ | 200 url to image (JPEG bytes served as image/png) |
| Docs claim: Generate a PDF of a page: POST /v1/pdf | api | done ✓ | 200 url to %PDF-1.4 |
| Docs claim: Auto-solve CAPTCHAs encountered during a session: POST /v1/sessions {solveCaptcha: true}; GET captchas/status; POST captchas/solve | api | plan_gated | 403 needs $10 paid balance |
| Docs claim: Route session traffic through residential or custom proxies with geo targeting: POST /v1/sessions {useProxy: {...}} | api | plan_gated | 403 needs $10 paid balance |
| Docs claim: Persist and reuse a browser profile (cookies, auth, extensions, settings) across sessions: POST /v1/sessions {persistProfile}; POST/PATCH /v1/profiles | api | done ✓ | persist+reuse works; PATCH only multipart; no DELETE |
| Docs claim: Store and auto-inject login credentials (incl. TOTP) into a session: POST /v1/credentials; POST /v1/sessions {credentials: {}} | api | done ✓ | injected and auto-submitted on our own origin |
| Scaffold a project from a template: steel forge playwright --name ad-bot | cli | done ✓ | "steel forge playwright" -> {"error":"Template not found: playwright"}; forge --list shows slug playwright-ts; "steel forge playwright-ts --name ad-bot" made ad-bot/ with index.ts, package.json, tsconfig.json, README.md |
| Install coding-agent skills: steel skills list | cli | done ✓ | skills list --json: steel-browser, steel-developer, ... (ga, 0.1.0, installed false); install goes through npx skills into agent client dirs: not installed (would write outside the run dir) |
| Self-host the browser runtime: steel dev install / Docker image | none | skipped | docs: /overview/self-hosting/docker, railway, clustering; CLI steel dev install/start |
| Session files: upload, list, download, delete a file in a live session | api | done ✓ | POST /v1/sessions/{id}/files multipart -> 201 /files/ad-test.txt 30 B; GET list shows it; download returns our text; DELETE 204; list -> [] |
| Global files: upload, list, download, delete an org file | api | done ✓ | POST /v1/files -> 201 agentdrives/ad-test.txt; list shows it; download matches; DELETE /v1/files/agentdrives%2Fad-test.txt 204; list -> [] |
| Extensions: upload our own MV3 zip, start a session with it, check its content script ran, delete | api | done ✓ | POST /v1/extensions 201 id ibeilnpl...; GET lists it; session extensionIds -> example.com has data-agentdrives="loaded"; DELETE 200 "Extension deleted successfully"; list count 0 |
| Agent traces: GET agent-traces, agent-logs, logs of a finished session | api | done ✓ | GET /v1/sessions/76a0d344/agent-traces 200 events navigate about:blank -> example.com ...; agent-logs 200; logs 200; cost 200 totalCost 0.001667 usd |
| Replay a past session: GET /v1/sessions/{id}/hls of a released session | api | done ✓ | 200 #EXTM3U playlist with init.mp4 on fly.storage.tigris.dev (signed URLs); GET /events returned [] for the same session |
| Mobile mode: session deviceConfig {device:'mobile'}, read navigator on example.com | api | partial ✓ | session dimensions 508x1074; UA "Mozilla/5.0 (Linux; Android 16; CPH2801) ... Mobile Safari"; innerWidth 500; but navigator.maxTouchPoints 0, userAgentData.mobile false, screen 1920x1080 |
| Fullscreen 1920x1080: session fullscreen:true | api | done ✓ | innerWidth x innerHeight 1920x1080 = screen 1920x1080 (no browser chrome) |
| Reuse auth context: GET /v1/sessions/{id}/context from a live session, pass it as sessionContext to a new one | api | partial ✓ | context 200 {cookies:[], localStorage:{"example.com":{"ad_ctx":...}}}; passed back as-is -> new session localStorage null; same value keyed "https://example.com" -> restored |
| Dedicated static IP: check docs/plan | none | plan_gated | llms.txt pricing: "dedicated IPs at $5 each" on paid plans; map plan paid; free Launch plan has no paid balance |
| Watch a session live: open the session page of a live session in the dashboard | browser | done ✓ | app.steel.dev/sessions/02f6726c: "Live" badge, live frame of example.com, event list (Navigation about:blank -> https://example.com/), cost 0.0016; CLI steel browser live returned the same URL |
| Human takes control: open the debugUrl player, click to interact, send keys | browser | done ✓ | api.steel.dev/v1/sessions/{id}/player: "Click to interact"; our click+Tab+Enter reached the remote Chrome (its screen shows "Bookmark added" on example.com) |
| Walk to the last click: Plans > Upgrade to Scale ($250/month + usage) | browser | done ✓ | Settings > Plans: Launch $0 + usage (current, $30 one-time credits), Scale $250 + usage, Enterprise Contact Sales; Upgrade to Scale opens "Add payment method" (Stripe Payment Element in-page: Card, Cash App Pay, US bank account; Google Pay frame); Card form: number, MM/YY, CVC, Country (Germany preset), no phone field; invisible hCaptcha frame from Stripe (HCaptchaInvisible); last button "Save payment method"; stopped there, no card typed |
| Agent rail: read pay_probe.json and pay_tx.json | api | done ✓ | x402.steel.dev/v1/scrape 402 PAYMENT-REQUIRED x402 v2, exact, Base eip155:8453 + Solana, 10000 USDC units ($0.01), resource api.paysponge.com/v1/scrape; we paid once: tx 0xdd2f191a...b20 on Base, confirmed block 52525353, 200 with example.com html (entitled) |
| View usage and balance: Settings > Usage and Wallet | browser | done ✓ | Usage: last 24h spend $0.04, sessions 36, browser minutes 10, proxy 0.00 GB, captcha solves 0, scrapes 4, hourly table, Export as CSV; header balance $29.96 of $30 |
| Top up credits: Wallet > Buy credits | browser | skipped | Buy credits dialog: $50 / $250 / $1,000 / Other, "Minimum purchase is $10. Usage purchases expire 1 year after purchase.", button "Continue to pay $50.00"; not clicked (no paying in a drive); auto top-up under "Configure" |
| MCP version: stdio server/discover with MCP-Protocol-Version 2026-07-28, then initialize | mcp | done ✓ | server/discover -> result.supportedVersions ["2026-07-28"], capabilities tools/resources, extension io.modelcontextprotocol/ui; initialize offering 2026-07-28 -> protocolVersion "2025-11-25", serverInfo steel / Steel Browser 3.0.0; tools/list 16 tools |
| Scrape a page: MCP steel_scrape example.com | mcp | done ✓ | tools/call steel_scrape -> "Read https://example.com/ (HTTP 200)" with the page text wrapped in <untrusted-page-content> |
| Create a cloud browser session: MCP steel_session_create, navigate, release | mcp | done ✓ | first call refused: "Saved profiles are available, so a fresh guest browser was not created. Choose one READY profile_id and retry, or pass guest=true"; with guest:true -> session_id sess_CVUH..., viewer 62ce5886; steel_navigate final_url https://example.com/ navigated true; steel_session_release released true; GET /v1/sessions/62ce5886 -> released |
| Docs claim: Scrape a page to HTML/markdown without managing a session: steel_scrape | mcp | done ✓ | HTTP 200 read of example.com |
| Docs claim: Create a cloud browser session: steel_session_create | mcp | done ✓ | needs guest=true when the org has saved profiles |
| Docs claim: Drive the session with Playwright/Puppeteer/Selenium over CDP: steel_navigate / steel_act | mcp | done ✓ | steel_navigate final_url example.com |
| Docs claim: Release/stop a browser session: steel_session_release | mcp | done ✓ | released true; API shows released |
| First API call: GET /v1/sessions with the GitHub account key | api | done ✓ | 200 {'sessions': [], 'nextCursor': None} |
| First action: POST /v1/scrape example.com | api | done ✓ | 200 title Example Domain |
| Keyless x402: pay one scrape call on x402.steel.dev | api | done ✓ | pay_tx.json: POST https://x402.steel.dev/v1/scrape, x402 v2 exact on Base, $0.01 USDC, tx 0xdd2f191a...4b03b20 confirmed block 52525353, HTTP 200 with example.com html |
| Stripe Projects: stripe projects add steel/browser | cli | skipped | documented at docs.steel.dev/overview/stripe-projects; listed in the projects.dev catalog |
| Enterprise SSO | none | skipped | llms.txt: Enterprise "SSO + HIPAA-ready BAA" (Contact Sales) |
| CLI login: steel login (CLI 0.4.4) with the magic-link account | cli | skipped | steel login prints https://app.steel.dev/sign-in?cli_redirect=true&port=<random>&state=•••• and waits on a localhost callback; opened in our logged-in browser, the redirect to localhost:<port> got ERR_CONNECTION_REFUSED |
| : SDK session chain | sdk | done ✓ | session 6f6352e2 live in 1023 ms |
| : SDK session chain | sdk | done ✓ | GET right after create: {'status': 'live', 'websocketUrl': True} read_errors None |
| : SDK session chain | sdk | done ✓ | goto example.com -> {'url': 'https://example.com/', 'title': 'Example Domain'}; click -> {'url': 'https://www.iana.org/help/example-domains', 'title': 'Example Domains'} |
| : SDK session chain | sdk | done ✓ | release {'success': True}; GET {'status': 'released', 'duration': 0} |
| Cancel plan: cancel/downgrade | none | skipped | no plan or trial started (Launch is $0 + usage; Scale $250/month needs a payment method). Docs (llms-full.txt) have no page on cancelling or downgrading; the dashboard has Plans and a "Manage billing" button (Stripe portal, not opened) |
| Scoped key headless: revoke the API-made key | browser | done ✓ | Settings > API Keys > Delete agentdrives-headless key > "This permanently revokes agentdrives-headless" > Yes, delete; the key then -> 401 {'error': 'Unauthorized', 'message': "Invalid Steel API key. The key sent in the 'steel-api-key' hea |
| Revoke GitHub grant: GitHub Settings > Applications > Steel.dev > Revoke access | browser | done ✓ | "Steel.dev has been revoked from your account."; Authorized OAuth Apps: "No authorized OAuth Apps" |
Also in Browser infrastructure.
BrowserlessL4Gets in on its ownSteel vs Browserless
Anchor BrowserL4Gets in on its ownSteel vs Anchor Browser
BrowserbaseL2Gets in on its ownSteel vs Browserbase