Guide

Getting Started

Install the package and scrape one page with Playwright. No key and no account for the first call
The API and the tool list can still move. Pin exact versions if you build on it now.

Why this exists

Every browser vendor has a different idea of what "get me this page" means. Steel does it in one POST. Kernel wants a live browser first and then runs Playwright inside it. Browserbase fetches without a session but hands you a CDP URL for anything else. Anchor doesn't scrape at all.

That's fine for you, you read the docs once. A model reads them never. So @agntn/browsers puts one scrape() over all of them, opens and closes the session when a vendor needs one, and lets every backend say what it can do through capabilities(). Same thing in TypeScript, in the terminal and in an agent.

Install

shell
pnpm add @agntn/browsers

Node.js 26 or newer.

First call

Playwright runs Chromium on your machine, so it needs no key. It takes the Chrome or Chromium already on your PATH. Without one, npx playwright-core install chromium gets it.

tsfirst.ts
import { create } from "@agntn/browsers";

const provider = await create("playwright");
const page = await provider.scrape("https://example.com");

page.title; // "Example Domain"
page.text; // "This domain is for use in documentation examples…"

create() is async on purpose. That's the moment the provider module gets imported, so importing the package costs nothing and a bundler splits every provider into its own chunk.

Who scrapes, and how

providers()8 providers · registry order
Needs
Steelsteelone call4 / 13STEEL_API_KEY12,348 chars of html
Browserbasebrowserbaseone call3 / 13BROWSERBASE_API_KEY209 chars of markdown
Kernelkernelin a session6 / 13KERNEL_API_KEY12,363 chars of html
Browserlessbrowserlessone call8 / 13BROWSERLESS_API_KEY12,364 chars of html
Hyperbrowserhyperbrowserone call7 / 13HYPERBROWSER_API_KEY167 chars of markdown
Anchoranchorno scrape3 / 13ANCHOR_API_KEYrefused, no page
Cloudflarecloudflareone call10 / 13CF_API_TOKEN and CF_ACCOUNT_ID886 chars of markdown
Playwrightplaywrightone call9 / 13no key917 chars of text
flags from the library / scrape of example.com recorded 2026-09-29ops counts a session-only one

A cloud provider needs its key in the environment, STEEL_API_KEY for Steel and so on. Cloudflare wants two, a token and an account ID. Providers has the full list and a page per vendor.

Let it pick

ts
import { create, resolveProvider } from "@agntn/browsers";

const provider = await create(resolveProvider());

resolveProvider() walks the registry in order and takes the first provider whose key is set. Steel if you set STEEL_API_KEY. Playwright if you set nothing, because it's last and never needs a key. Pass a name and it checks that one instead, and throws AuthError when its key is missing, not something else quietly.

Where next

  • Scrape for what comes back and why it isn't the same everywhere.
  • CLI if you live in a terminal.
  • MCP, Pi and OMP to hand all of this to a model.