How do I connect Puppeteer to Lightpanda Cloud using browserWSEndpoint, and how do I set the region?
Headless Browser Infrastructure

How do I connect Puppeteer to Lightpanda Cloud using browserWSEndpoint, and how do I set the region?

8 min read

Most Puppeteer stacks hit the same bottleneck in the cloud: Headless Chrome is slow to cold-start, heavy on memory, and expensive to scale when you’re driving hundreds or thousands of concurrent browser sessions. Lightpanda Cloud exists to fix exactly that, without forcing you to rewrite your scripts—Puppeteer just connects over CDP using browserWSEndpoint.

This guide shows, step by step, how to:

  • Connect Puppeteer to Lightpanda Cloud using browserWSEndpoint
  • Choose the right region (euwest vs uswest)
  • Keep your existing Puppeteer code with minimal changes
  • Layer on responsible crawling defaults

Quick overview: what changes in your Puppeteer code

In a regular Puppeteer setup you typically do:

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  headless: "new",
  // ...other options
});

With Lightpanda Cloud, you don’t launch a local browser. Instead you connect to a managed, headless-only browser via CDP:

import puppeteer from "puppeteer-core";

const browser = await puppeteer.connect({
  browserWSEndpoint: "wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN",
});

// The rest of your script remains the same.
const page = await browser.newPage();

Everything else—page.goto, page.evaluate, selectors, waits—stays unchanged. You’re swapping the engine under the hood, not the way you write automation logic.

1. Prerequisites

Before you connect Puppeteer to Lightpanda Cloud, you’ll need:

  • A Lightpanda Cloud account with an API token
    • You’ll get a token from the Lightpanda console.
    • Keep it secret; treat it like any other production credential.
  • Node.js and Puppeteer in your project
    • Prefer puppeteer-core for CDP-only setups:
      npm install puppeteer-core
      
  • A region choice
    • euwest – typically better if your workloads or target sites are closer to Europe.
    • uswest – typically better for US West / Americas–adjacent workloads.

Latency matters for automation. Running the browser closer to your target domains reduces total execution time (especially when you’re doing millions of pages a day).

2. Connect Puppeteer with browserWSEndpoint

The key primitive is puppeteer.connect({ browserWSEndpoint }).

Basic Cloud connection example

import puppeteer from "puppeteer-core";

async function main() {
  const token = process.env.LPD_TOKEN; // store your token in env
  if (!token) {
    throw new Error("Missing LPD_TOKEN environment variable");
  }

  // Region: euwest (see next section for uswest)
  const browserWSEndpoint =
    `wss://euwest.cloud.lightpanda.io/ws?token=${token}`;

  const browser = await puppeteer.connect({ browserWSEndpoint });

  const page = await browser.newPage();
  await page.goto("https://example.com", { waitUntil: "networkidle0" });

  const title = await page.title();
  console.log("Page title:", title);

  await browser.close();
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Key points:

  • Use puppeteer.connect, not puppeteer.launch.
  • browserWSEndpoint is a standard CDP WebSocket URL.
  • The token is passed as a query parameter to authenticate your session.
  • Once connected, you use browser exactly like in a local Puppeteer flow.

3. How to set the region (euwest vs uswest)

Region selection is encoded directly in the hostname of the browserWSEndpoint.

Region endpoints

You can currently choose between:

  • wss://euwest.cloud.lightpanda.io/ws
  • wss://uswest.cloud.lightpanda.io/ws

You append your token as a query parameter:

wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN
wss://uswest.cloud.lightpanda.io/ws?token=YOUR_TOKEN

Region-aware Puppeteer config

A simple pattern is to parameterize the region via environment variables:

import puppeteer from "puppeteer-core";

async function main() {
  const token = process.env.LPD_TOKEN;
  if (!token) throw new Error("Missing LPD_TOKEN");

  const region = process.env.LPD_REGION || "euwest"; // default to euwest

  // Validate region
  if (!["euwest", "uswest"].includes(region)) {
    throw new Error(`Unsupported LPD_REGION: ${region}`);
  }

  const browserWSEndpoint =
    `wss://${region}.cloud.lightpanda.io/ws?token=${token}`;

  const browser = await puppeteer.connect({ browserWSEndpoint });

  const page = await browser.newPage();
  await page.goto("https://example.com", { waitUntil: "networkidle0" });

  // Do your magic ✨
  const html = await page.content();
  console.log("HTML length:", html.length);

  await browser.close();
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Now you can deploy the same code to different regions simply by changing LPD_REGION:

LPD_TOKEN=xxx LPD_REGION=euwest node index.js
LPD_TOKEN=xxx LPD_REGION=uswest node index.js

4. Migrating from a local Headless Chrome setup

If you’re currently spinning up Headless Chrome locally, the minimal diff to use Lightpanda Cloud looks like this.

Before: local launch

import puppeteer from "puppeteer";

async function main() {
  const browser = await puppeteer.launch({
    headless: "new",
    args: ["--no-sandbox"],
  });

  const page = await browser.newPage();
  await page.goto("https://example.com");

  await browser.close();
}

After: Cloud connect with browserWSEndpoint

import puppeteer from "puppeteer-core";

async function main() {
  const token = process.env.LPD_TOKEN;
  if (!token) throw new Error("Missing LPD_TOKEN");

  const region = process.env.LPD_REGION || "euwest";
  const browserWSEndpoint =
    `wss://${region}.cloud.lightpanda.io/ws?token=${token}`;

  const browser = await puppeteer.connect({ browserWSEndpoint });

  const page = await browser.newPage();
  await page.goto("https://example.com");

  await browser.close();
}

The rest of your Puppeteer script—navigation, scraping, testing flows—stays untouched. That’s deliberate: we built Lightpanda around CDP so you can keep your existing tooling and just change where the browser lives.

5. Why this matters for large-scale automation

When you’re automating at scale—crawling, testing, or driving agents—two numbers dominate your cloud bill and your reliability:

  • Cold-start time: Every new browser process that takes multiple seconds to start becomes a tax you pay across thousands of sessions.
  • Memory peak per browser: Multiply 200+ MB by hundreds of concurrent sessions and you quickly hit the ceiling of a single node or cluster.

Lightpanda Cloud runs a browser that was built from scratch in Zig, specifically for headless automation:

  • No UI or rendering stack
  • Instant startup
  • Minimal memory footprint

In our own Puppeteer 100‑page benchmark on an AWS EC2 m5.large:

  • Execution time: 2.3s with Lightpanda vs 25.2s with Headless Chrome
    → ~11× faster
  • Memory peak: 24MB vs 207MB
    → ~9× less memory

You feel these gains immediately when you switch your browserWSEndpoint from a local Chrome instance to a Lightpanda Cloud endpoint—and you don’t have to touch the rest of your script.

6. Responsible crawling with Lightpanda Cloud

Performance cuts both ways: when startup is instant and memory is low, it’s easy to accidentally overload a site.

When you connect Puppeteer to Lightpanda Cloud using browserWSEndpoint, keep the same operational hygiene you would with any large-scale crawler:

  • Respect robots.txt
    • In self-hosted Lightpanda you can enforce this with --obey_robots.
    • In Cloud, implement checks at your job scheduler / URL queue level.
  • Throttle concurrency
    • Limit per-domain concurrency and add small delays when necessary.
    • DDOS-level traffic is trivial to generate by accident when your browser starts instantly.
  • Centralize rate limits
    • If you’re fanning out many Puppeteer workers, enforce global caps per target domain.

Lightpanda is optimized for scale, but scale needs guardrails.

7. Troubleshooting common connection issues

A few issues I’ve seen repeatedly when teams first connect Puppeteer to Lightpanda Cloud:

7.1 Invalid or missing token

Symptom: Connection fails immediately.

  • Ensure the token is present:
    echo $LPD_TOKEN
    
  • Confirm you’re passing it as a query parameter in the URL:
    const browserWSEndpoint =
      `wss://euwest.cloud.lightpanda.io/ws?token=${token}`;
    

7.2 Wrong region hostname

Symptom: DNS errors or failed handshake.

  • Check for typos:
    • Correct: wss://euwest.cloud.lightpanda.io/ws
    • Correct: wss://uswest.cloud.lightpanda.io/ws
  • Avoid stray slashes or missing wss://.

7.3 Using puppeteer instead of puppeteer-core in minimal environments

While you can use puppeteer, I recommend puppeteer-core when you’re only connecting via CDP. It avoids bundling and managing a local Chrome binary you’re never going to launch.

npm install puppeteer-core

8. Putting it all together: a small “real-world” snippet

Here’s a compact, production-style script that:

  • Selects region via env
  • Connects using browserWSEndpoint
  • Scrapes the <title> tag for a list of URLs
import puppeteer from "puppeteer-core";

const URLs = [
  "https://example.com",
  "https://www.wikipedia.org",
];

async function run() {
  const token = process.env.LPD_TOKEN;
  if (!token) throw new Error("Missing LPD_TOKEN");

  const region = process.env.LPD_REGION || "euwest";
  if (!["euwest", "uswest"].includes(region)) {
    throw new Error(`Unsupported region: ${region}`);
  }

  const browserWSEndpoint =
    `wss://${region}.cloud.lightpanda.io/ws?token=${token}`;

  const browser = await puppeteer.connect({ browserWSEndpoint });

  try {
    const page = await browser.newPage();

    for (const url of URLs) {
      await page.goto(url, { waitUntil: "networkidle0" });
      const title = await page.title();
      console.log(`[${region}] ${url} => ${title}`);
    }
  } finally {
    await browser.close();
  }
}

run().catch((err) => {
  console.error(err);
  process.exit(1);
});

If you want multi-region coverage, you can run this script in parallel with different LPD_REGION values, or factor region into your job queue.

9. Where this fits into your broader stack

Once Puppeteer is talking to Lightpanda Cloud over browserWSEndpoint, you can:

  • Swap in Playwright or chromedp using the same CDP endpoint pattern:
    • Playwright example from the docs:
      import playwright from "playwright-core";
      
      const browser = await playwright.chromium.connectOverCDP(
        "wss://euwest.cloud.lightpanda.io/ws?token=TOKEN",
      );
      const context = await browser.newContext();
      const page = await context.newPage();
      
  • Mix Lightpanda and Chrome in the same infrastructure if you need Chrome for edge compatibility while keeping Lightpanda as the default for high-volume workloads.
  • Move from Cloud to self-hosted (or vice versa) by just changing the browserWSEndpoint URL—from wss://…cloud.lightpanda.io/ws to ws://127.0.0.1:9222, for example.

The connection primitive remains:

puppeteer.connect({ browserWSEndpoint: CDP_URL });

Everything else is just which browser engine sits on the other end of that WebSocket.

Next step

If you want to see how this feels in your own stack, wire up browserWSEndpoint to Lightpanda Cloud, set a region close to your workloads, and run one of your existing Puppeteer flows unchanged. That’s usually all it takes to experience instant startup and lower memory footprints in practice.

Get Started