If you already use Playwright, browser.city is a drop-in infrastructure layer:
- Create a session with
POST /v1/sessions - Connect with
chromium.connect(endpoint, { headers: { Authorization: Bearer token } }) - Run your existing Playwright script
1) Set your API key
Set BROWSERCITY_API_KEY in your environment.
2) Create a session and connect
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", // Optional: pick an exit location and proxy type egress: { mode: "managed", proxyType: "residential", country: "US" }, // Optional: keep sessions easy to filter in your dashboard labels: ["playwright", "prod"], }),}).then((r) => r.json());const browser = await chromium.connect(session.endpoint, { headers: { Authorization: `Bearer ${session.token}` },});const context = await browser.newContext();const page = await context.newPage();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", "egress": {"mode": "managed", "proxyType": "residential", "country": "US"}, "labels": ["playwright", "prod"], },).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", egress = new { mode = "managed", proxyType = "residential", country = "US" }, labels = new[] { "playwright", "prod" }, });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 page = await browser.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.*;// Create session via your preferred HTTP clientvar 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 session = res.body();try (var pw = Playwright.create()) { var browser = pw.chromium().connect( extractEndpoint(session), new BrowserType.ConnectOptions().setHeaders( java.util.Map.of("Authorization", "Bearer " + extractToken(session)))); var page = browser.newPage(); page.navigate("https://example.com");}static String extractEndpoint(String json) { return ""; }static String extractToken(String json) { return ""; }
4) Practical patterns
Use Request API for pure extraction
If you don’t need interaction, the Request API is simpler than keeping a browser alive:
const apiKey = process.env.BROWSERCITY_API_KEY!;const opts = { method: "POST", headers: { Authorization: `Bearer ${apiKey}` } };const res = await fetch("https://api.browser.city/v1/requests", { ...opts, body: JSON.stringify({ url: "https://example.com", markdown: true }),}).then((r) => r.json());console.log(res.content);import osimport requestsapi_key = os.environ["BROWSERCITY_API_KEY"]res = requests.post( "https://api.browser.city/v1/requests", headers={"Authorization": f"Bearer {api_key}"}, json={"url": "https://example.com", "markdown": True},).json()print(res["content"])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/requests", new { url = "https://example.com", markdown = true });Console.WriteLine(await res.Content.ReadAsStringAsync());import java.net.URI;import java.net.http.*;var apiKey = System.getenv("BROWSERCITY_API_KEY");var http = HttpClient.newHttpClient();var req = HttpRequest.newBuilder() .uri(URI.create("https://api.browser.city/v1/requests")) .header("Authorization", "Bearer " + apiKey) .POST(HttpRequest.BodyPublishers.ofString( "{\"url\":\"https://example.com\",\"markdown\":true}")) .build();var res = http.send(req, HttpResponse.BodyHandlers.ofString());System.out.println(res.body());
BYOP proxy
If you already have proxy infrastructure, bring your own:
Use egress: { mode: "byop", connectionString: "http://user:pass@host:port" } in your POST /v1/sessions body.
Pre-seed auth via storage state
If you already have cookies/localStorage from another run, inject them at session creation time via storage so you don’t re-login every time.
5) Migration note (local -> browser.city)
Most migrations are mechanical:
- replace
chromium.launch()withchromium.connect(...) - move your “browser boot” concerns into
POST /v1/sessions(egress, fingerprint, storage) - keep the rest of your Playwright code unchanged