Quick verdict: Browserbase is a broad AI-native platform: browsers plus Search and Fetch APIs, agents (Stagehand), runtime, identity features, a model gateway, and observability. If you want that breadth and don’t mind tiered features and “browser-hours” pricing, it’s a strong incumbent. If your workloads live on hostile, bot-protected surfaces and you want high-fidelity stealth by default, zero-logs by design, and pricing you can calculate in your head, browser.city is built for that.
What you’re actually choosing
Both products give you managed, remote browsers you control via APIs. But the platforms have diverged:
- Browserbase has grown into an AI-native product suite: Browser-as-a-Service plus Search and Fetch web-data APIs (launched March 2026), the Stagehand agent framework, an agent runtime, identity/CAPTCHA features, a model gateway, observability tooling, MCP, and a CLI.
- browser.city concentrates on one thing being exceptional: proprietary high-fidelity browser simulation — a browser that maintains a coherent real-user identity across device, network, and interaction signals — wrapped in controlled, private, transparently priced infrastructure.
At a glance
| Dimension | browser.city | Browserbase |
|---|---|---|
| Stealth | High-fidelity, on by default — proven on Google-, Cloudflare-, and DataDome-protected surfaces | Stealth/CAPTCHA features often tiered by plan |
| Pricing model | Clear (simple usage model, public rates) | Browser-hours plans + tiering across bundled products |
| API modes | Playwright sessions, Request API, MCP, Humanized REST tools | Browsers, Search & Fetch APIs, Stagehand SDK, agent runtime, MCP, CLI |
| Privacy posture | Zero-logs architecture | Varies by product (verify against their docs/security pages) |
| Best for | Identity-sensitive and high-block-rate workloads with predictable costs | Teams that want a broad AI-native “platform” experience |
Stealth: fidelity vs feature checkbox
If your workload hits bot mitigation (Cloudflare / DataDome / PerimeterX / Kasada), the question isn’t “do you have stealth?” but:
- Is it enabled by default?
- Is it high-fidelity — a coherent identity across device, network, and interaction signals, not a patched headless browser?
- Can you control fingerprint + egress without a matrix of add-ons?
browser.city’s posture is simple: stealth is on, and it is built on a proprietary custom browser rather than automation patches — with results demonstrated on surfaces protected by Google, Cloudflare, and DataDome. You can still tune sessions (fingerprints, storage state, managed egress, BYOP), but the baseline is designed to work on hostile surfaces.
Pricing: what’s easy to estimate wins
When teams migrate off competitors, the most common reason is not performance, it’s billing complexity:
- credits vs units vs multipliers
- stealth priced as an “upgrade”
- proxy bandwidth billed separately with unclear ratios
Browserbase pricing is generally expressed in browser-hours with tiered plans. That can be totally fine, but you’ll want to model:
- expected concurrency
- long-running sessions
- proxy/bandwidth costs (if separate)
browser.city aims for “you can calculate it in your head” pricing and exposes a Request API for workloads that don’t need full browser orchestration.
API surface: four ways to ship
browser.city intentionally supports four “entry points,” depending on how agentic you need to be:
- Request API: fetch a URL and return clean markdown (fastest path to data pipelines).
- Sessions API: create a session and connect using Playwright.
- Humanized REST tools (
/v1/do/*): click/type/markdown over HTTP without running Playwright. - MCP server: give tools to coding agents and AI frameworks without writing glue.
Example: create a session and connect with Playwright:
import { chromium } from 'playwright';const { endpoint, token, id } = await fetch('https://api.browser.city/v1/sessions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.BROWSERCITY_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ browser: 'chromium' }),}).then((r) => r.json());try { const browser = await chromium.connect(endpoint, { headers: { Authorization: `Bearer ${token}` }, }); const page = browser.contexts().at(0)!.pages().at(0)!; await page.goto('https://example.com'); console.log(await page.title());} finally { await fetch(`https://api.browser.city/v1/sessions/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.BROWSERCITY_API_KEY}` }, });}using Microsoft.Playwright;using System.Net.Http.Headers;using System.Net.Http.Json;var http = new HttpClient();http.DefaultRequestHeaders.Authorization = new( "Bearer", Environment.GetEnvironmentVariable("BROWSERCITY_API_KEY"));var session = await (await http.PostAsJsonAsync( "https://api.browser.city/v1/sessions", new { browser = "chromium" })) .Content.ReadFromJsonAsync<Session>();var (id, endpoint, token) = session!;IPlaywright? pw = null;try { pw = await Playwright.CreateAsync(); var browser = await pw.Chromium.ConnectAsync(endpoint, new() { Headers = new() { ["Authorization"] = $"Bearer {token}" } }); var page = browser.Contexts[0].Pages[0]; await page.GotoAsync("https://example.com");} finally { try { if (session is not null) await http.DeleteAsync($"https://api.browser.city/v1/sessions/{id}"); } finally { pw?.Dispose(); }}record Session(string Id, string Endpoint, string Token);import com.fasterxml.jackson.databind.ObjectMapper;import com.microsoft.playwright.*;import java.net.URI;import java.net.http.*;import java.util.Map;public class Session { public static void main(String[] args) throws Exception { var key = System.getenv("BROWSERCITY_API_KEY"); var http = HttpClient.newHttpClient(); var req = HttpRequest.newBuilder(URI.create("https://api.browser.city/v1/sessions")) .header("Authorization", "Bearer %s".formatted(key)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"browser\":\"chromium\"}")) .build(); var created = new ObjectMapper().readValue( http.send(req, HttpResponse.BodyHandlers.ofString()).body(), CreatedSession.class); var id = created.id(); var endpoint = created.endpoint(); var token = created.token(); Playwright pw = null; try { pw = Playwright.create(); var browser = pw.chromium().connect(endpoint, new BrowserType.ConnectOptions().setHeaders(Map.of("Authorization", "Bearer %s".formatted(token)))); var page = browser.contexts().get(0).pages().get(0); page.navigate("https://example.com"); } finally { try { http.send(HttpRequest.newBuilder(URI.create("https://api.browser.city/v1/sessions/" + id)) .header("Authorization", "Bearer %s".formatted(key)).DELETE().build(), HttpResponse.BodyHandlers.discarding()); } finally { if (pw != null) pw.close(); } } } record CreatedSession(String id, String endpoint, String token) {}}
When to pick which
Choose Browserbase if:
- you want an AI-first product suite (Search, Fetch, Stagehand, runtime, model gateway) under one roof
- you’re already invested in their SDK ecosystem
- you’re OK with stealth/identity features being plan-gated
Choose browser.city if:
- your targets sit behind serious bot mitigation and you need high-fidelity stealth by default, not as an upgrade
- you care about a zero-logs posture as an architectural constraint
- you want multiple integration styles (Request API, Playwright sessions, Humanized REST, MCP tools) without switching products
Migration notes (Browserbase → browser.city)
Most migrations are mechanical:
- Swap session creation to
POST /v1/sessions - Keep your Playwright code, but connect using the returned
endpointandtokenheader - For “fetch + extract” flows, replace agent loops with
POST /v1/requestsorPOST /v1/requests/batch
If you want help mapping your current flows (sessions, egress, and fingerprint strategy), start with the Integrations hub and pick your toolchain.