Quick verdict: Steel.dev and browser.city are closer peers than most comparisons: both are developer-first, Playwright-native, and agent-friendly. The practical decision comes down to billing ergonomics, privacy posture, and whether you want extraction and agent tools in the same surface area as your browser sessions.
What’s the same
Both products are built for modern “agent + browser” workflows:
- Playwright-compatible remote browsers
- emphasis on developer experience
- integrations that assume AI-assisted automation is the default, not the edge case
If you’re already happy with Steel.dev’s reliability and cost model, you may not need to switch.
What’s different (and shows up in week 2)
1) Billing shape
Steel.dev commonly uses a credit model (often denominated in browser-hours). Credit models are not inherently bad, but they tend to add friction when:
- you want to predict cost per job
- you have mixed workloads (fast extraction + long sessions)
- you run large retry storms (anti-bot targets, flaky sites)
browser.city is built around clear pricing primitives and a Request API that’s intentionally cheaper to use than spinning up full sessions for “fetch + extract” jobs.
2) Privacy posture: policy vs architecture
Most browser infra providers publish policies. Fewer design the system so session content can’t be retained as a default path.
browser.city’s public position is zero-logs by design: no session recording pipeline, no content-at-rest capture, and minimal metadata. If your automation touches sensitive data (internal tools, customer portals, finance), this distinction matters.
3) Product surface: extraction + agents + sessions
browser.city deliberately supports:
- Request API (
/v1/requests,/v1/requests/batch) to return clean markdown - Sessions API (
/v1/sessions) for long-running Playwright automation - MCP server for agent clients (Codex, Claude Code, Cursor, etc.)
Example: batch extract multiple URLs in one shared session:
const res = await fetch("https://api.browser.city/v1/requests/batch", { method: "POST", headers: { Authorization: `Bearer ${process.env.BROWSERCITY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ requests: ["https://example.com", "https://example.org"].map((url) => ({ url, markdown: true })), }),}).then((r) => r.json());console.log(res.successCount, res.errorCount);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 res = await (await http.PostAsJsonAsync( "https://api.browser.city/v1/requests/batch", new { requests = new[] { "https://example.com", "https://example.org" }.Select(url => new { url, markdown = true }) })) .Content.ReadFromJsonAsync<BatchResponse>();Console.WriteLine($"{res?.SuccessCount} ok, {res?.ErrorCount} failed");record BatchResponse(int SuccessCount, int ErrorCount);import com.fasterxml.jackson.databind.ObjectMapper;import java.net.URI;import java.net.http.*;public class Batch { public static void main(String[] args) throws Exception { var req = HttpRequest.newBuilder(URI.create("https://api.browser.city/v1/requests/batch")) .header("Authorization", "Bearer %s".formatted(System.getenv("BROWSERCITY_API_KEY"))) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"requests\":[{\"url\":\"https://example.com\",\"markdown\":true},{\"url\":\"https://example.org\",\"markdown\":true}]}")) .build(); var res = new ObjectMapper().readValue( HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()).body(), BatchResponse.class); System.out.println(res.successCount() + " ok, " + res.errorCount() + " failed"); } record BatchResponse(int successCount, int errorCount) {}}
When to pick which
Choose Steel.dev if:
- you want a very polished interactive platform experience
- you’re already standardized on their billing model and integrations
- you don’t need a separate “Request API” style primitive
Choose browser.city if:
- you care about zero-logs as an architectural guarantee
- you want extraction workloads to be first-class (markdown, batch, metadata)
- you want agents to work through MCP without glue code