Quick verdict: This isn’t a pure head-to-head. Browser Use is primarily an agent framework (plus hosted execution). browser.city is browser infrastructure. Many teams use both: Browser Use for the agent loop, browser.city for the stealth browser backend.
Different layers of the stack
Think of the stack as three layers:
- Agent loop (planning, tool selection, retries, memory)
- Browser tools (navigate, click, fill, screenshot, extract)
- Browser runtime + stealth + egress (fingerprints, proxies, anti-bot)
Browser Use focuses on (1) and part of (2).
browser.city focuses on (2) and (3):
- MCP server exposes browser tools to any client
- Sessions API provides Playwright-compatible browsers
- Request API provides extraction without orchestration
At a glance
| Dimension | browser.city | Browser Use |
|---|---|---|
| Primary product | Browser infrastructure | Agent framework + hosted agent execution |
| Control style | Deterministic APIs (Playwright/REST/MCP tools) | Agentic abstraction + steps |
| Stealth | Default | Varies by tier / setup |
| Best for | Teams building agents and scrapers that need reliable infra | Teams that want agent loop primitives and an OSS ecosystem |
How to combine them (common pattern)
- Use Browser Use to decide what to do next.
- Use browser.city to provide the actual browser and extraction primitives.
If your agent framework can talk to Playwright, you can create a browser.city session and connect using the returned endpoint/token header.
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 Browser Use if:
- you want an OSS-first agent framework and community-driven patterns
- you want higher-level agent abstractions (steps, skills, workflows)
Choose browser.city if:
- you want infrastructure primitives you can plug into any agent/tooling stack
- you need stealth-by-default and strong egress/fingerprint control
- you want Request API for cheap “URL → markdown” flows