
Browser To Api
Reverse-engineer HTTP APIs from captured browser traffic and emit OpenAPI specs with a local Swagger UI preview for solo builders wiring agents or backends to sites without public docs.
Overview
Browser to API is an agent skill for the Build phase that turns browser-captured traffic into OpenAPI specs and local Swagger UI previews.
Install
npx skills add https://github.com/browserbase/skills --skill browser-to-apiWhat is this skill?
- Node ESM stage pipeline: load → filter → normalize → infer → emit via discover.mjs
- Partial reruns with discover.mjs --stage for debugging intermediate artifacts
- Emits openapi.yaml/json under run api-spec with open-swagger-ui.mjs preview
- Serves Swagger UI dist with injected swagger-initializer.js on a local HTTP origin
- Depends only on Node standard library for core stage scripts
- Five-stage default pipeline: load, filter, normalize, infer, emit
Adoption & trust: 1.1k installs on skills.sh; 3.5k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You can use a product in the browser but lack a trustworthy OpenAPI document to build clients, tests, or agent tools against its HTTP surface.
Who is it for?
Solo builders documenting third-party or own web-app APIs from observed network behavior when you control or are authorized to test the target.
Skip if: Teams that need production API gateways, legal gray-area scraping of sites you do not own, or MCP-only server discovery with no browser capture step.
When should I use this skill?
You have browser/session capture material and need OpenAPI plus a local spec preview before implementing API clients.
What do I get? / Deliverables
You get a generated openapi.yaml or openapi.json plus an optional local Swagger UI session to validate routes before implementing integrations.
- openapi.yaml or openapi.json under the run api-spec directory
- Runnable stage artifacts from partial --stage reruns
- Local Swagger UI preview session via open-swagger-ui.mjs
Recommended Skills
Journey fit
API discovery and spec generation happen while you are integrating external services into a product, before you hard-code clients. Fits integrations because the pipeline turns browser-observed calls into machine-readable OpenAPI for the next coding step.
How it compares
Use instead of hand-typing OpenAPI from DevTools screenshots when you want a repeatable Node pipeline and spec preview.
Common Questions / FAQ
Who is browser-to-api for?
Indie and solo developers building integrations, internal tools, or agent workflows who need OpenAPI from real browser sessions and prefer scriptable Node stages over one-off manual transcription.
When should I use browser-to-api?
During Build integrations while mapping an HTTP surface from captures, before coding API clients; also when validating an emitted spec locally before ship-time contract tests.
Is browser-to-api safe to install?
Review the Security Audits panel on this Prism page and inspect the Node scripts and any browser/open steps before running against non-local targets.
SKILL.md
READMESKILL.md - Browser To Api
{ "name": "browser-to-api", "version": "0.1.0", "private": true, "type": "module" } # Browser to API — Reference Exhaustive reference for every script, flag, file format, and configuration knob the skill exposes. ## Scripts All scripts are Node ESM (`type: module`). They depend only on the Node standard library. `discover.mjs` is the top-level dispatcher; the others are stage scripts the dispatcher calls in order. Run an individual stage with `discover.mjs --stage <name>` for debugging or partial reruns. ### `discover.mjs --run <path> [flags]` Top-level dispatcher. Runs `load → filter → normalize → infer → emit` in order. With `--stage <name>`, runs only that stage (assumes prior stages already wrote their intermediate file). ### `open-swagger-ui.mjs (--run <path> | --spec <path>) [flags]` Preview an emitted OpenAPI spec in a local Swagger UI checkout. The script serves the Swagger UI `dist/` assets and the generated spec from one local HTTP origin, injects a per-run `swagger-initializer.js`, opens the browser by default, and keeps the server alive until interrupted. - `--run <path>` loads `<run>/api-spec/openapi.yaml`, falling back to `openapi.json`. - `--spec <path>` previews an explicit OpenAPI YAML/JSON file. - `--swagger-ui <path>` points at a Swagger UI checkout/package directory. If omitted, the script tries `$SWAGGER_UI_DIR`, `~/Developer/swagger-ui`, and `node_modules/swagger-ui-dist`. - `--host <host>` defaults to `127.0.0.1`. - `--port <port>` defaults to a random free port. - `--no-open` prints the URL without opening a browser. ### `load.mjs <run-path> <out-dir> [bodies-dir]` - Reads `cdp/network/requests.jsonl` and `cdp/network/responses.jsonl`. - Pairs by `requestId`. Drops `OPTIONS` (CORS preflight) and pure redirects (status 3xx with `Location` and no body — recorded as metadata on the *next* request in the chain when the requestId carries forward, otherwise dropped). - Drops resource types that are not `XHR`, `Fetch`, or `Document` (skips `Image`, `Stylesheet`, `Font`, `Media`, `Manifest`, `Other`, `Script` unless the URL clearly looks like an API endpoint). - **Body join**: if a `browse network` capture dir is provided (via `--bodies` or auto-detected at `<run>/cdp/network/bodies/`), each subdir's `request.json` + `response.json` are read and joined to paired rows by `requestId`. The browse-network `id` field IS the CDP requestId for XHR/Fetch resource types, so the join is exact (not URL-or-timestamp matching). Bodies that look like JSON are parsed; otherwise the raw string is preserved. - Output: `intermediate/paired.jsonl` — one row per pair with `{ method, url, status, reqHeaders, reqBody, respHeaders, respBody, contentType, type, ts }`. ### `filter.mjs <run-path>` - Reads `intermediate/paired.jsonl`. - Applies `--include` / `--exclude` / `--origins`. - Applies built-in exclude list (analytics hosts, sourcemaps, service workers, fonts/CSS that snuck through). - Output: `intermediate/filtered.jsonl`. ### `normalize.mjs <run-path>` - Templatizes paths. Detection order per segment: 1. UUID v1–v5 → `{id}` (`string`, `format: uuid`). 2. Pure integer → `{id}` (`integer`). 3. Hex/base62 ≥ 8 chars → `{id}` (`string`). 4. If the same position varies across multiple samples and is short alpha → `{slug}` (`string`). 5. Otherwise the segment is left static. - Groups paired samples by `(origin, method, templatedPath)`. - Collects query parameters across samples; marks `required: true` only when every sample carries the param. - If two pre-normalization templates would collapse but yield divergent response status/content-type signatures, they're kept split and flagged. - Output: `intermediate/endpoints.jsonl` — one row per endpoint with `{ origin, method, path, samples[], queryParams, statusCodes, normalizationFlags }`. ### `infer.mjs <run-path>` - For each endpoint, runs JSON-Schema inference across request bodies and (when present) response bodies. - Merge rules: required = presen