Browser mode
Match a session's clock, language and Accept-Language to the country its proxy comes out in, or set the locale and time zone yourself.
A session’s browser runs in our cluster, so by default its clock is UTC and its language is en-US. If the session’s proxy comes out in Germany, that is an odd mix: a German address with an American clock.
Browser mode fixes the mix. With "browser": { "mode": "realistic" } the browser’s clock, language and Accept-Language follow the proxy’s country, so a session leaving from Berlin also says it is in Berlin. You can also set the locale and time zone yourself.
The two modes
mode | What the browser gets |
|---|---|
"standard" (default) | The cluster’s own settings: UTC and en-US. Nothing is changed for you. |
"realistic" | The clock, language and Accept-Language of the proxy’s country, narrowed by the US state or the city when the proxy names one. |
There is no lookup of your IP address: the location comes from the proxy settings you already gave us, so nothing extra leaves the platform.
- No managed proxy (no proxy at all, or only your own
customproxy):"realistic"changes nothing, because we do not know where the traffic comes out. - A managed proxy without a country: nothing changes either. Name a
countryto get a matching clock and language. - A country we have no entry for: the language is left alone, and so is the clock unless the proxy names a city we know. The platform has entries for 62 countries, 29 US states and the cities whose time zone differs from their country’s.
- Rules by site: the catch-all rule (the one without a
domainPattern) decides, because it carries most of the traffic. With no catch-all rule, the first residential or datacenter rule in the list decides; if that one names no country, nothing changes.
A German session
Pass browser as an object when you create the session. An object always means “give this session a browser”, so you do not need browser: true as well.
import { Boxline } from "@boxline/sdk";
const client = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URL
const session = await client.sessions.create({
proxy: { type: "residential", country: "DE" },
browser: { mode: "realistic" },
});
try {
await session.goto("https://example.com");
// { mode: "realistic", locale: "de-DE", timezone: "Europe/Berlin" }
console.log(session.data.browserSettings);
} finally {
await session.release();
}from boxline import Boxline
bx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URL
with bx.sessions.create(
proxy={"type": "residential", "country": "DE"},
browser_options={"mode": "realistic"},
) as session:
session.goto("https://example.com")
# {"mode": "realistic", "locale": "de-DE", "timezone": "Europe/Berlin"}
print(session.browser_settings)
# leaving the block releases the sessioncurl -X POST https://api.staging.boxline.dev/v1/sessions \
-H "x-api-key: $BOXLINE_API_KEY" \
-H "content-type: application/json" \
-d '{
"proxy": {"type": "residential", "country": "DE"},
"browser": {"mode": "realistic"}
}'Inside that browser, new Date() is Berlin time, navigator.language is de-DE, dates and numbers format the German way, and every request carries Accept-Language: de-DE,de;q=0.9,en;q=0.8.
Some more locations and what they give you:
| Proxy | timezone | locale |
|---|---|---|
{ "country": "DE" } | Europe/Berlin | de-DE |
{ "country": "FR" } | Europe/Paris | fr-FR |
{ "country": "JP" } | Asia/Tokyo | ja-JP |
{ "country": "BR" } | America/Sao_Paulo | pt-BR |
{ "country": "US" } | America/New_York | en-US |
{ "country": "US", "state": "us_california" } | America/Los_Angeles | en-US |
{ "country": "US", "city": "chicago" } | America/Chicago | en-US |
A country gets its main time zone. Where a country has several (the US, Canada, Australia, Brazil, Russia), the proxy’s state or city narrows it down; without one, the most populated zone is used. The language follows the country, not the city.
Options
| Field | Default | Description |
|---|---|---|
mode | "standard" | "standard" or "realistic". Anything else is a 400. |
locale | none | A language tag (BCP 47), e.g. "de-DE", "pt-BR", "fr". Sets the browser’s language and Accept-Language. |
timezone | none | An IANA time zone, e.g. "Europe/Berlin", "America/Los_Angeles". Sets the browser’s clock. |
browser still takes true (a browser with the defaults) and false (no browser). An object means the session has a browser.
Set the locale and time zone yourself
locale and timezone win over the mode, in either mode. Give one and let the mode fill in the other, or give both and the mode changes nothing.
// A Swiss address, but German-language pages and Zurich time
const session = await client.sessions.create({
proxy: { type: "residential", country: "CH" },
browser: { locale: "de-CH", timezone: "Europe/Zurich" },
});
// Realistic mode for the clock, your own language for the pages
const english = await client.sessions.create({
proxy: { type: "residential", country: "JP" },
browser: { mode: "realistic", locale: "en-US" },
});# A Swiss address, but German-language pages and Zurich time
session = bx.sessions.create(
proxy={"type": "residential", "country": "CH"},
browser_options={"locale": "de-CH", "timezone": "Europe/Zurich"},
)
# Realistic mode for the clock, your own language for the pages
english = bx.sessions.create(
proxy={"type": "residential", "country": "JP"},
browser_options={"mode": "realistic", "locale": "en-US"},
)curl -X POST https://api.staging.boxline.dev/v1/sessions \
-H "x-api-key: $BOXLINE_API_KEY" \
-H "content-type: application/json" \
-d '{
"proxy": {"type": "residential", "country": "CH"},
"browser": {"locale": "de-CH", "timezone": "Europe/Zurich"}
}'A value the browser would not accept is refused with 400 invalid_request before the session starts, for example "timezone": "Nowhere/Nothing" or "locale": "german".
What the session reports
Every session shows browserSettings: the mode you asked for, and the values actually in force. null means the browser keeps its own default.
"proxy": { "type": "residential", "country": "DE", "ip": "sticky", "scope": "browser" },
"browserSettings": {
"mode": "realistic",
"locale": "de-DE",
"timezone": "Europe/Berlin"
}A session with no proxy and no options shows { "mode": "standard", "locale": null, "timezone": null }. In the SDKs: session.data.browserSettings (TypeScript) and session.browser_settings (Python).
Where it applies
- Every tab, including tabs the page opens by itself and tabs you open later with Playwright, Puppeteer or the Actions API.
- After a move, a resume, or a recovery. The new machine gets the same clock and language, so a long-running session keeps one story. Tabs that come back are reopened first and get the settings a moment later; reload one if its first view matters. See Move, pause & resume.
- Playground scripts and agent runs that work in the session, because they drive the same browser.
Change it while the session runs
Send browser to PATCH/v1/sessions/:id. Open tabs follow within a second or two, and new tabs get the new settings.
// Switch to realistic mode
await session.update({ browser: { mode: "realistic" } });
// Or set the language yourself, keeping the proxy's clock
await session.update({ browser: { mode: "realistic", locale: "en-GB" } });
console.log(session.data.browserSettings);# Switch to realistic mode
session.update(browser={"mode": "realistic"})
# Or set the language yourself, keeping the proxy's clock
session.update(browser={"mode": "realistic", "locale": "en-GB"})
print(session.browser_settings)curl -X PATCH https://api.staging.boxline.dev/v1/sessions/$SESSION_ID \
-H "x-api-key: $BOXLINE_API_KEY" \
-H "content-type: application/json" \
-d '{"browser": {"mode": "realistic"}}'- Send the whole object. It replaces the earlier one: a field you leave out goes back to its default.
{}returns the session to"standard". - The proxy carries the clock with it. In realistic mode, changing the proxy (see Change or remove the proxy) moves the clock and language to the new country too. Removing the proxy drops them back to the browser’s defaults; values you set yourself stay.
- A paused session takes the change and uses it when it resumes.
- Errors:
400 invalid_requestfor a session without a browser or a value the browser would not accept;409 session_not_runningonce the session has ended.
Fetch, screenshot, PDF, extract and crawl
POST/v1/fetch, POST/v1/screenshot, POST/v1/pdf, POST/v1/extract and POST/v1/crawl take the same browser object. It applies to the fresh browser context that renders each page; a crawl applies it to every page it loads.
const page = await client.fetch("https://example.com", {
format: "markdown",
proxy: { type: "residential", country: "DE" },
browser: { mode: "realistic" },
});
const png = await client.screenshot("https://example.com", {
proxy: { type: "residential", country: "DE" },
browser: { mode: "realistic" },
});
// Every page of the crawl gets the German clock and language
const job = await client.crawl.start({
url: "https://example.com",
maxPages: 50,
proxy: { type: "residential", country: "DE" },
browser: { mode: "realistic" },
});page = bx.fetch(
"https://example.com",
proxy={"type": "residential", "country": "DE"},
browser={"mode": "realistic"},
)
png = bx.screenshot(
"https://example.com",
proxy={"type": "residential", "country": "DE"},
browser={"mode": "realistic"},
)
# Every page of the crawl gets the German clock and language
job = bx.crawl.start(
"https://example.com",
max_pages=50,
proxy={"type": "residential", "country": "DE"},
browser={"mode": "realistic"},
)curl -X POST https://api.staging.boxline.dev/v1/fetch \
-H "x-api-key: $BOXLINE_API_KEY" \
-H "content-type: application/json" \
-d '{
"url": "https://example.com",
"format": "markdown",
"proxy": {"type": "residential", "country": "DE"},
"browser": {"mode": "realistic"}
}'These APIs always use a browser, so true and false change nothing there; give the object.
Agent runs
POST/v1/agent/runs takes browser for the session the run creates: true (the default), false for a run without a browser, or the options object. With sessionId, the run uses that session’s settings; change them with PATCH first.
const run = await client.agent.run({
task: "Find the delivery cost for a sofa to Munich on example.de",
proxy: { type: "residential", country: "DE" },
browser: { mode: "realistic" },
});run = bx.agent.run(
"Find the delivery cost for a sofa to Munich on example.de",
proxy={"type": "residential", "country": "DE"},
browser={"mode": "realistic"},
)curl -X POST https://api.staging.boxline.dev/v1/agent/runs \
-H "x-api-key: $BOXLINE_API_KEY" \
-H "content-type: application/json" \
-d '{
"task": "Find the delivery cost for a sofa to Munich on example.de",
"proxy": {"type": "residential", "country": "DE"},
"browser": {"mode": "realistic"}
}'Why this helps
Sites look at the whole picture. A German IP address with a New York clock and an American language header is a combination a real visitor rarely has, and some sites treat it as a reason to show a check, a different price, or the wrong language.
A consistent story is simply what a real visitor looks like. It also makes the page you get the page a local would get: the right currency, the right delivery options, the right opening hours.
Pair it with the rest:
- Residential proxies in the country you are working in, with a sticky IP for the whole task.
- Contexts, so the account’s cookies come back with it and the site sees a visitor it knows.
- A slower pace. Fewer pages per minute does more for you than any browser setting. See Meet fewer CAPTCHAs.
What it does not do
We would rather be plain about this than let you find out later.
- No fingerprint spoofing. Boxline does not offer it. The browser reports its real user agent, screen, fonts, canvas, GPU and everything else. The
Accept-Languageheader is set with the browser’s own user agent passed back unchanged. - It does not hide automation. A site that checks for an automated browser will still see one.
- It is not a way past bot detection. If a site blocks you, browser mode is not the answer; a CAPTCHA still needs a person, and a site that does not allow automated access should not be automated.
- It does not change your IP address. That is the proxy. Browser mode only matches the browser to it.
Whether the fleet runs Chrome headless or a visible Chromium on a virtual screen is a property of the machine image: it is the same for every session, and there is no per-session option for it.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.