← back to blog

Integrating Playwright with Multilogin via local API

Integrating Playwright with Multilogin via local API

Running browser automation through a standard Playwright install is fine until you hit fingerprint checks, canvas fingerprint blocking, or platforms that flag headless Chrome outright. I ran into this wall after about three weeks of scraping e-commerce data, where sessions kept getting flagged despite rotating proxies. The fix wasn’t the proxy layer, it was the browser fingerprint.

Multilogin solves this by spinning up isolated browser profiles with unique fingerprints, separate cookie stores, and configurable user agents. Their local API, which runs as a REST service on your machine when the Multilogin app is open, lets you start those profiles programmatically and retrieve a Chrome DevTools Protocol (CDP) endpoint. Playwright can then connect to that endpoint and drive the profile as if it were a normal browser session, with all the fingerprint spoofing handled transparently.

This tutorial is for operators who already know Playwright basics and want to integrate it with Multilogin X (the current version as of early 2026). By the end you’ll have a working Node.js script that authenticates against the local API, starts a profile, connects Playwright over CDP, and performs actions inside a fingerprinted session. I’ll also cover what breaks at scale.

what you need

  • Multilogin X subscription (Starter plan starts at roughly $99/month as of 2026, billed annually, check current pricing at multilogin.com)
  • Multilogin desktop app installed and running on your machine, logged in
  • Node.js 18+ and npm
  • Playwright installed (npm install playwright)
  • axios or built-in fetch for local API calls
  • At least one browser profile created in the Multilogin UI (Mimic for Chromium-based, Stealthfox for Firefox-based)
  • Basic familiarity with async/await in JavaScript
  • A working proxy assigned to your profile is optional but strongly recommended for production use

step by step

step 1: confirm the local API is running

The Multilogin X local API runs on http://localhost:45000 when the desktop app is open. Before writing any code, verify it responds.

curl http://localhost:45000/status

Expected output: a JSON response with {"status":"ok"} or similar. If you get a connection refused error, the Multilogin app is either not running or still initialising. On slower machines give it 20-30 seconds after launch.

If it breaks: check your firewall isn’t blocking 127.0.0.1:45000. On Windows, run the Multilogin app as administrator once to let it register the port.

step 2: authenticate and get a token

All Multilogin local API calls require a bearer token. You get one by posting your credentials.

const axios = require('axios');

const BASE = 'http://localhost:45000';

async function getToken(email, password) {
  const res = await axios.post(`${BASE}/user/signin`, {
    email,
    password
  });
  return res.data.token;
}

The token is a JWT that stays valid for the session. Store it in memory, not on disk. If the app restarts you’ll need a new one.

If it breaks: a 401 means wrong credentials. A 500 often means the app hasn’t finished loading the user session yet. Add a retry with a 5-second delay.

step 3: list your profiles and grab a profile ID

You need the UUID of the profile you want to automate. You can find it in the Multilogin UI under the profile name, or pull it via API.

async function listProfiles(token) {
  const res = await axios.get(`${BASE}/profile`, {
    headers: { Authorization: `Bearer ${token}` }
  });
  return res.data.data; // array of profile objects
}

Each profile object contains uuid, name, browser, and os fields. Log the list and copy the UUID you want to use. Hardcode it for testing, then make it dynamic later.

If it breaks: if data.data is undefined, check the response structure, Multilogin has occasionally changed the nesting between minor versions.

step 4: start the browser profile in automation mode

This is the key call. The automation=true query parameter tells Multilogin to expose the CDP debugger URL instead of opening a visible window for user interaction.

async function startProfile(token, profileId) {
  const res = await axios.get(`${BASE}/profile/${profileId}/start`, {
    params: { automation: true },
    headers: { Authorization: `Bearer ${token}` }
  });
  return res.data.wsEndpoint; // something like ws://127.0.0.1:PORT/...
}

The response includes a wsEndpoint field, a WebSocket URL pointing to the CDP server for that profile. This is what Playwright needs.

If it breaks: if you get a 404, double-check the profile UUID. If you get “profile already running”, either stop it in the UI first or handle it by connecting directly if the endpoint is still live.

step 5: connect Playwright over CDP

Playwright’s connectOverCDP is designed exactly for this use case. It attaches to an existing Chrome instance rather than launching a new one.

const { chromium } = require('playwright');

async function connectToProfile(wsEndpoint) {
  const browser = await chromium.connectOverCDP(wsEndpoint);
  const context = browser.contexts()[0]; // Multilogin creates a default context
  const page = context.pages()[0] || await context.newPage();
  return { browser, context, page };
}

Note: don’t call browser.newContext() here. Multilogin manages the context with all its fingerprint settings. Creating a new context bypasses those settings and you’ll lose the spoofing.

If it breaks: if Playwright throws “Target page, context or browser has been closed”, the profile may have crashed during start. Check Multilogin’s own logs at %APPDATA%\[Multilogin](https://multilogin.com/) X\logs on Windows or ~/.multiloginx/logs on Linux/Mac.

step 6: write your automation logic

Now you have a normal Playwright page object inside a fingerprinted profile.

async function run() {
  const token = await getToken('[email protected]', 'yourpassword');
  const profiles = await listProfiles(token);
  const profileId = profiles[0].uuid; // or pick by name

  const wsEndpoint = await startProfile(token, profileId);
  const { browser, page } = await connectToProfile(wsEndpoint);

  await page.goto('https://browserleaks.com/canvas');
  await page.screenshot({ path: 'canvas_check.png' });

  console.log('Done. Browser stays open for inspection.');
  // Don't close browser here unless you want to kill the profile session
}

run().catch(console.error);

Run this with node script.js. The screenshot should show a randomised canvas fingerprint, not the raw Chromium default.

If it breaks: if the page loads but the fingerprint looks like vanilla Chrome, confirm the profile has fingerprint settings applied in the Multilogin UI and that you’re using a Mimic profile, not a “Raw” one.

step 7: stop the profile cleanly

When you’re done, stop the profile via API rather than just closing the browser object. Playwright’s browser.close() disconnects the CDP session but may leave the Multilogin profile process running.

async function stopProfile(token, profileId) {
  await axios.get(`${BASE}/profile/${profileId}/stop`, {
    headers: { Authorization: `Bearer ${token}` }
  });
}

Call this in a finally block so it runs even if your automation errors out.

If it breaks: if stop returns 400, the profile may already be stopped. Wrap in a try/catch and log rather than throw.

step 8: put it together with error handling

const axios = require('axios');
const { chromium } = require('playwright');

const BASE = 'http://localhost:45000';
let profileId;

async function main() {
  const token = await getToken(process.env.ML_EMAIL, process.env.ML_PASSWORD);
  const profiles = await listProfiles(token);
  profileId = profiles.find(p => p.name === 'My Profile').uuid;

  const wsEndpoint = await startProfile(token, profileId);
  const { page } = await connectToProfile(wsEndpoint);

  try {
    await page.goto('https://example.com');
    // your logic here
  } finally {
    await stopProfile(token, profileId);
  }
}

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

Store credentials in environment variables, never hardcode them. Use a .env file with dotenv locally.


common pitfalls

Using browser.newContext() instead of the existing context. This is the most common mistake I see. Playwright makes it easy to spin up new contexts, but in a Multilogin session the fingerprint configuration lives in the context Multilogin creates. A fresh context from Playwright has a fresh fingerprint, i.e., the spoofed one is gone.

Forgetting automation=true on the start call. Without this flag Multilogin opens the profile in a regular window for manual use. The CDP port is not exposed, and Playwright’s connectOverCDP will hang or timeout.

Sharing a single token across parallel processes. The JWT itself is fine to share, but if two scripts try to start the same profile simultaneously you’ll get a conflict error. Use one profile per worker and track which profiles are in use.

Not stopping profiles after runs. Multilogin counts active profiles against plan limits. If your script crashes mid-run without a finally block, profiles stay running and you’ll hit limits faster than expected. This is especially painful at scale.

Running Playwright’s auto-close on the browser object. Some Playwright patterns automatically close the browser at the end of scope. When connected over CDP, closing the browser object terminates the CDP connection but the Multilogin process may linger. Always call the stop API explicitly.


scaling this

10 profiles: run them sequentially or with a simple Promise.all. The local API handles this fine. Your bottleneck is the Multilogin app’s memory usage, roughly 200-400MB per active Chromium profile. A 16GB machine handles 10 comfortably.

100 profiles: you need multiple machines or VMs at this point. The Multilogin desktop app is designed for single-machine use, so you’ll run one instance per machine and distribute profiles across them. Each machine needs its own Multilogin login session and local API. A simple coordinator service that knows which machine holds which profiles is enough to get started. For proxy management at this scale, I’ve found it useful to read through proxyscraping.org’s blog for bulk proxy sourcing strategies.

1000 profiles: you’re looking at a fleet of maybe 10-20 machines depending on profile activity pattern. At this point the manual profile creation workflow in the Multilogin UI doesn’t scale. You need to use the Multilogin profile creation API to programmatically generate profiles with specific fingerprint parameters. Logging and error recovery also become critical. A dead profile on machine 7 at 3am is silent unless you have alerting. Consider wrapping each run in a structured logger and piping to a centralised log store. The multi-account operations patterns covered at multiaccountops.com/blog are worth reading before you invest in fleet infrastructure.

The Playwright documentation on parallel testing has useful patterns for worker allocation that translate reasonably well to multi-profile setups, even though you’re not using Playwright Test here.


where to go next

If you’re also running airdrop or DeFi workflows on top of these profiles, airdropfarming.org’s blog has some practical notes on session management that complement this setup.

The Multilogin API documentation is the authoritative reference. It’s reasonably well maintained and the local API section covers parameters I didn’t go into here like browser version pinning and custom DNS settings per profile.


Written by Xavier Fok

disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-05-19.

need infra for this today?