cdpfleet Docs GitHub Dashboard

Cases / #25 · 2026-10-07 · Easy

No proxy of your own: rotating, country and sticky residential IPs with one token

Four launches on the cdpfleet residential proxy — any country, Germany, and one sticky US identity carried from one browser session to the next — with nothing but a token as the proxy.

Chromium

Run on production on 2026-10-07: ✓ Node.js ✓ Python ✓ Java ✓ C# ✓ Go

The problem

Every cdpfleet session needs a proxy, and until now that meant buying one somewhere else first. Residential IPs are what most scraping and account work needs: real ISP addresses in the right country, a new one per request for crawling, or the same one for a whole login flow. The cdpfleet residential proxy puts all of that in the proxy field — cdpfleet-resi, plus country, device OS and a sticky session name — billed from a prepaid balance. Does the targeting hold, does a sticky IP really survive into the next browser, and what does it look like from the site's side?

What we used, and why

WhatWhy
"proxy": "cdpfleet-resi"A new residential IP for every connection, any country. No credentials anywhere in your code: the upstream is configured on our servers.
-country-deExit in Germany; any ISO two-letter code works.
-session-<name>-lifetime-10A sticky session: every connection with the same name uses the same IP for up to 10 minutes. Names are private to your account.
Two browser sessions with the same sticky nameBrowser 2 starts after browser 1 has closed — the question is whether the identity carries over.
http://ip-api.com/json three times per browserThree separate connections: rotating exits should change, sticky ones shouldn't. Each lookup is retried — residential peers drop a few percent of connections.
GET /v1/me/proxy-balanceThe prepaid balance the traffic is billed to ($5/GB, 1 GB free on every account).

How it works

  1. Launch Chromium four times with four tokens: cdpfleet-resi, cdpfleet-resi-country-de, and twice cdpfleet-resi-country-us-session-<name>-lifetime-10.
  2. In each browser, look up the exit IP and country three times.
  3. Compare: how many distinct IPs each browser saw, which countries, and whether the second sticky browser got the first one's IP.
  4. Read the balance and price from the Account API.

The code

The same program in five languages (also on GitHub, with the raw output). Set these environment variables first:

// npm install [email protected]
// env: CDPFLEET_API_KEY (no proxy of your own needed)
import { chromium } from 'playwright';

const KEY = process.env.CDPFLEET_API_KEY;
const SESSION = `c25${Date.now().toString(36).slice(-6)}`; // a sticky name, 1–10 of [A-Za-z0-9_]

// Four launches, all on the cdpfleet residential proxy — the token is the whole proxy config.
const RUNS = [
  { label: 'rotating, any country', proxy: 'cdpfleet-resi' },
  { label: 'rotating, Germany', proxy: 'cdpfleet-resi-country-de' },
  { label: 'sticky US, browser 1', proxy: `cdpfleet-resi-country-us-session-${SESSION}-lifetime-10` },
  { label: 'sticky US, browser 2', proxy: `cdpfleet-resi-country-us-session-${SESSION}-lifetime-10` },
];

// Residential peers drop a few percent of connections: retry a lookup up to 3 times.
async function lookup(page, i) {
  for (let attempt = 1; ; attempt++) {
    try {
      return await (await page.goto(`http://ip-api.com/json/?fields=query,countryCode&i=${i}-${attempt}`, { timeout: 60000 })).json();
    } catch (err) {
      if (attempt === 3) throw err;
    }
  }
}

async function run({ label, proxy }) {
  const res = await fetch('https://starter.cdpfleet.com/chromium/session', {
    method: 'POST',
    headers: { 'x-api-key': KEY, 'content-type': 'application/json' },
    body: JSON.stringify({ proxy, headless: 'new' }),
  });
  if (!res.ok) return { label, error: `launch ${res.status} ${await res.text()}` };
  const { wsUrl } = await res.json();
  const browser = await chromium.connect(wsUrl, { headers: { 'x-api-key': KEY } });
  try {
    const page = await browser.newPage();
    // Three lookups on separate connections: rotating exits change, sticky ones don't.
    const exits = [];
    for (let i = 0; i < 3; i++) exits.push(await lookup(page, i));
    return {
      label,
      proxy: proxy.replace(SESSION, '<name>'),
      countries: [...new Set(exits.map((e) => e.countryCode))].join(','),
      distinct_ips: new Set(exits.map((e) => e.query)).size,
      first_ip: exits[0].query,
    };
  } finally {
    await browser.close();
  }
}

const out = [];
for (const r of RUNS) out.push(await run(r)); // in order, so browser 2 starts after browser 1 ended
const [b1, b2] = out.slice(2);
for (const r of out) r.same_ip_as_browser_1 = r.label.startsWith('sticky') ? r.first_ip === b1.first_ip : null;
// The balance the traffic is billed to (metered about once a minute, so it trails a little).
const bal = await (await fetch('https://cdpfleet.com/v1/me/proxy-balance', { headers: { 'x-api-key': KEY } })).json();
for (const r of out) delete r.first_ip;
console.log(JSON.stringify({ runs: out, sticky_ip_kept_across_browsers: b2.same_ip_as_browser_1, balance_allowed: bal.allowed, price_per_gb_usd: bal.price_per_gb_usd }, null, 2));

What we got

LaunchproxyExit countriesIPs in 3 lookupsSame IP as browser 1
rotating, any countrycdpfleet-resiIT,FR,IN3—
rotating, Germanycdpfleet-resi-country-deDE3—
sticky US, browser 1cdpfleet-resi-country-us-session-<name>-lifetime-10US1yes
sticky US, browser 2cdpfleet-resi-country-us-session-<name>-lifetime-10US1yes

From the Node.js run on 2026-10-07. IP addresses are replaced with placeholders (203.0.113.x); equal addresses stay equal. The other languages produced the same findings.

Raw output (Node.js)
{
  "runs": [
    {
      "label": "rotating, any country",
      "proxy": "cdpfleet-resi",
      "countries": "IT,FR,IN",
      "distinct_ips": 3,
      "same_ip_as_browser_1": null
    },
    {
      "label": "rotating, Germany",
      "proxy": "cdpfleet-resi-country-de",
      "countries": "DE",
      "distinct_ips": 3,
      "same_ip_as_browser_1": null
    },
    {
      "label": "sticky US, browser 1",
      "proxy": "cdpfleet-resi-country-us-session-<name>-lifetime-10",
      "countries": "US",
      "distinct_ips": 1,
      "same_ip_as_browser_1": true
    },
    {
      "label": "sticky US, browser 2",
      "proxy": "cdpfleet-resi-country-us-session-<name>-lifetime-10",
      "countries": "US",
      "distinct_ips": 1,
      "same_ip_as_browser_1": true
    }
  ],
  "sticky_ip_kept_across_browsers": true,
  "balance_allowed": true,
  "price_per_gb_usd": 5
}

Takeaways