Logo of Lexmount
DocsTemplatesBlogHelp Center
GitHub
Logo of LexmountLexmount Browser

Get started

Lexmount Documentation
Choose an interface and connectBrowser modes and compatibilityPlaywright Cloud Browser — Remote CDP ConnectionConnect with PuppeteerBrowser CLI for agents and terminalsSession lifecycle and cleanupInteract and wait for resultsExtract HTML, text, and linksReuse a persistent ContextConfigure a browser proxyAccess local networks with the SDKTroubleshooting by stage

Browser

Web Fetch

OverviewQuick Start
Common Errors

Help and billing

Capability FAQCloud Browser Pricing and Web Fetch API Billing
ChangelogContact support
Guides

Access local networks with the SDK

Use a local proxy tunnel from Node.js or Python to reach company, VPN, and development sites from Cloud Browser.

Run the SDK on a computer that can reach your target site. A normal cloud browser can then send its web traffic through that SDK process. The SDK machine resolves destination names with its operating system DNS, including company/VPN DNS, and opens the TCP connections.

This is a Cloud Browser session feature. It does not add local-network access to the separate Web Fetch API.

Requirements

  • Local proxy was introduced in Node.js and Python SDK 0.6.0; use 0.6.1 or later. Set LEXMOUNT_API_KEY and LEXMOUNT_PROJECT_ID as described in the Node.js or Python quickstart.
  • The project must have custom_proxy capability, and the selected region must have local proxy enabled.
  • Use browserMode: 'normal' / browser_mode="normal". Light/Moli sessions do not support this mode.
  • Keep the tunnel and browser in the same project, API key, and region. Reuse the same client; optionally set LEXMOUNT_REGION to a region ID from catalogInfo() / catalog_info().
  • The SDK machine needs outbound access to the service's HTTPS and WSS endpoints and to the target site. No inbound listening port, public IP, or cloud-side hosts entry is needed. Keep the SDK process and VPN running throughout the session.

Node.js Example

Install dependencies and save the TypeScript example as local-proxy.ts:

npm install lexmount@^0.6.1 playwright
npm install --save-dev tsx
export TARGET_URL='https://your-internal-site.example/'
npx tsx local-proxy.ts

Use an ESM project ("type": "module" in package.json) for the top-level await example. Replace the target with an HTTP(S) URL reachable from the SDK machine and export your credentials first.

import { Lexmount } from 'lexmount';
import { chromium } from 'playwright';

const target = process.env.TARGET_URL;
if (!target) throw new Error('Set TARGET_URL to a reachable HTTP(S) URL');
const client = new Lexmount({ region: process.env.LEXMOUNT_REGION });
try {
  const tunnel = await client.tunnels.open();
  try {
    const session = await client.sessions.create({
      browserMode: 'normal',
      proxy: { type: 'local', tunnelId: tunnel.id },
    });
    try {
      const browser = await chromium.connectOverCDP(session.connectUrl);
      try {
        const context = browser.contexts()[0];
        if (!context) throw new Error('No browser context');
        const page = context.pages()[0] ?? await context.newPage();
        await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60_000 });
        console.log(await page.title());
      } finally {
        await browser.close();
      }
    } finally {
      await session.close();
    }
  } finally {
    await tunnel.close();
  }
} finally {
  client.close();
}

Python Example

python3 -m pip install --upgrade 'lexmount>=0.6.1' playwright
export TARGET_URL='https://your-internal-site.example/'
python3 local_proxy.py

Save this as local_proxy.py and export your credentials first:

import os
from lexmount import Lexmount
from playwright.sync_api import sync_playwright

target = os.environ["TARGET_URL"]
with Lexmount(region=os.getenv("LEXMOUNT_REGION") or None) as client:
    with client.tunnels.open() as tunnel:
        with client.sessions.create(
            browser_mode="normal",
            proxy={"type": "local", "tunnel_id": tunnel.id},
        ) as session:
            with sync_playwright() as playwright:
                browser = playwright.chromium.connect_over_cdp(session.connect_url)
                try:
                    if not browser.contexts:
                        raise RuntimeError("No browser context")
                    context = browser.contexts[0]
                    page = context.pages[0] if context.pages else context.new_page()
                    page.goto(target, wait_until="domcontentloaded", timeout=60_000)
                    print(page.title())
                finally:
                    browser.close()

The browser runs in the cloud; these examples connect to it over CDP. A local Chromium installation is not needed for this connection.

Tunnel Options and Lifecycle

Node.jsPythonBehavior
client.tunnels.open()client.tunnels.open()Return after the connector is ready
tunnel.idtunnel.idPass into the session proxy configuration
tunnel.regionIdtunnel.region_idRegion selected for the tunnel
tunnel.readytunnel.readyWhether the connector is currently ready
reconnectreconnectOpen option; defaults to enabled
connectTimeoutMsconnect_timeoutOpen option; local DNS/TCP timeout, default 15,000 ms / 15 seconds
await tunnel.close()tunnel.close()Close connections and revoke the tunnel

For example, client.tunnels.open({ connectTimeoutMs: 20_000 }) or client.tunnels.open(connect_timeout=20.0). These are connection deadlines, not page-navigation timeouts. Python options use seconds; Node.js uses milliseconds.

Close the browser connection, then the cloud session, then the tunnel, and finally the SDK client. The examples preserve this order when navigation fails. Closing a tunnel alone does not close the browser session or stop its billing.

Reconnect is enabled by default. A disconnect fails existing TCP streams; after reconnection only new connections use the restored tunnel. The connector does not replay requests or form submissions, and does not fall back to cloud DNS or another proxy. Verify the outcome of a write before retrying it. A tunnel expires after seven days and supports up to 128 concurrent TCP streams; that is a tunnel connection limit, not a browser-session quota.

Scope and Billing

  • Supported browser traffic includes HTTP, HTTPS, WS, and WSS over TCP. This is not a general UDP/VPN interface, and browser restrictions still apply.
  • Select one of local proxy, external proxy, or officialProxy / official_proxy; they are mutually exclusive.
  • The tunnel provides network reachability, not a copy of local cookies, logged-in browser state, or certificate trust. Login and private CA handling remain part of the cloud browser workflow.
  • Local egress is accounted for separately from official-proxy traffic. Normal browser time, concurrency, and project capability limits still apply.

Troubleshooting

SymptomCheck
tunnels is missingInstalled SDK version; upgrade to 0.6.1 or later
403 when opening a tunnelProject capability and the matching Key/Project ID
503 / local proxy unavailableWhether local proxy is enabled in the selected region
Tunnel and browser region mismatchUse the same client/region and a newly opened tunnel
Tunnel never becomes readyOutbound WSS access, VPN/firewall restrictions, and current credentials
Navigation timeout / ERR_TUNNEL_CONNECTION_FAILEDKeep the connector alive; verify the target's DNS and port from the SDK machine, then check tunnel readiness
Certificate or login errorCloud browser trust and authentication; local network access does not import these

A ready tunnel or created session proves only that stage succeeded; validate access to the actual target. When a tunnel expires, create a new tunnel and new sessions using its ID.

Maintained Demos

After the Node.js examples setup, run:

npm run local-proxy-demo -- --url 'https://your-internal-site.example/'

After the Python examples setup, run:

python3 local_proxy_demo.py --url 'https://your-internal-site.example/'

Both demos accept --region or read LEXMOUNT_REGION from .env. Pass the target through --url; these demo scripts do not read TARGET_URL. They save local_proxy_demo.png and wait for a keypress before cleanup.

Configure a browser proxy

Configure a browser proxy.

Troubleshooting by stage

Troubleshooting by stage.

Table of Contents

Requirements
Node.js Example
Python Example
Tunnel Options and Lifecycle
Scope and Billing
Troubleshooting
Maintained Demos

Still need help?Ask on Discord