Quick verdict: If you want a polished AI-native platform and don’t mind tiered stealth features and “browser-hours” pricing, Browserbase is a strong incumbent. If you want stealth enabled by default, zero-logs by design, and an API surface that stays simple as you scale (Playwright + Request API + Humanized REST + MCP), browser.city is built for that.
What you’re actually choosing
Both products live in the same category: managed, remote browsers you control via APIs (Playwright/CDP style flows).
The difference is in the defaults:
- Browserbase optimizes for an AI-native product ecosystem and polished onboarding.
- browser.city optimizes for infrastructure ergonomics: stealth and privacy as a baseline, and fewer “gotchas” in cost and integration.
At a glance
| Dimension | browser.city | Browserbase |
|---|---|---|
| Stealth | On by default | Stealth features often tiered by plan |
| Pricing model | Transparent (simple usage model) | Browser-hours plans + tiering |
| API modes | Playwright sessions, Request API, MCP, Humanized REST tools | Primarily Playwright/CDP-style workflows + SDKs |
| Privacy posture | Zero-logs architecture | Varies by platform (verify against their docs/security pages) |
| Best for | Teams that want predictable infra primitives | Teams that want an AI-native “platform” experience |
Stealth: defaults vs tiers
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 consistent across tiers and environments?
- Can you control fingerprint + egress without a matrix of add-ons?
browser.city’s posture is simple: stealth is on. 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 apiKey = process.env.BROWSERCITY_API_KEY!;const opts = { method: "POST", headers: { Authorization: `Bearer ${apiKey}` } };const session = await fetch("https://api.browser.city/v1/sessions", { ...opts, body: JSON.stringify({ browser: "chromium" }),}).then((r) => r.json());const browser = await chromium.connect(session.endpoint, { headers: { Authorization: `Bearer ${session.token}` },});const [context] = browser.contexts();const [page] = context.pages();await page.goto("https://example.com");import osimport requestsfrom playwright.sync_api import sync_playwrightapi_key = os.environ["BROWSERCITY_API_KEY"]session = requests.post( "https://api.browser.city/v1/sessions", headers={"Authorization": f"Bearer {api_key}"}, json={"browser": "chromium"},).json()with sync_playwright() as p: browser = p.chromium.connect( session["endpoint"], headers={"Authorization": f"Bearer {session['token']}"}, ) page = browser.new_page() page.goto("https://example.com")using Microsoft.Playwright;using System.Net.Http.Headers;using System.Net.Http.Json;var apiKey = Environment.GetEnvironmentVariable("BROWSERCITY_API_KEY")!;var http = new HttpClient();http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);var res = await http.PostAsJsonAsync( "https://api.browser.city/v1/sessions", new { browser = "chromium" });var session = await res.Content.ReadFromJsonAsync<Session>() ?? throw new Exception("bad response");using var pw = await Playwright.CreateAsync();var browser = await pw.Chromium.ConnectAsync(session.endpoint, new() { Headers = new Dictionary<string, string> { ["Authorization"] = $"Bearer {session.token}", },});var context = await browser.NewContextAsync();var page = await context.NewPageAsync();await page.GotoAsync("https://example.com");public record Session(string endpoint, string token);import com.microsoft.playwright.*;import java.net.URI;import java.net.http.*;import java.util.regex.*;var apiKey = System.getenv("BROWSERCITY_API_KEY");var http = HttpClient.newHttpClient();var res = http.send( HttpRequest.newBuilder() .uri(URI.create("https://api.browser.city/v1/sessions")) .header("Authorization", "Bearer " + apiKey) .POST(HttpRequest.BodyPublishers.ofString("{\"browser\":\"chromium\"}")) .build(), HttpResponse.BodyHandlers.ofString());var endpoint = extractJsonString(res.body(), "endpoint");var token = extractJsonString(res.body(), "token");try (var pw = Playwright.create()) { var browser = pw.chromium().connect( endpoint, new BrowserType.ConnectOptions().setHeaders( java.util.Map.of("Authorization", "Bearer " + token))); var page = browser.newPage(); page.navigate("https://example.com");}static String extractJsonString(String json, String key) { var m = Pattern.compile("\"" + key + "\"\\s*:\\s*\"([^\"]+)\"").matcher(json); if (!m.find()) throw new RuntimeException("missing " + key); return m.group(1);}
When to pick which
Choose Browserbase if:
- you want an AI-first product suite and polished platform UX
- you’re already invested in their SDK ecosystem
- you’re OK with stealth/compliance features being plan-gated
Choose browser.city if:
- you want stealth by default with fewer configuration “traps”
- 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.