← back to blog

Using Singapore mobile proxies with Puppeteer in 2026

puppeteer mobile proxies tutorials 2026

Using Singapore mobile proxies with Puppeteer in 2026

you already have a Puppeteer script that works fine on your dev machine. then you deploy it against a target in Southeast Asia and the block rate climbs to 60%, session cookies stop persisting, and Cloudflare starts throwing JS challenges on every third request. the culprit is almost always the IP. datacenter ranges get flagged at the ASN level before your script even sends a User-Agent header, and most VPN exits have been burned by previous abuse. what you actually need is an IP that looks like a real mobile subscriber in the right country, because that is what the legitimate users of those platforms look like. this guide covers wiring Puppeteer to Singapore Mobile Proxy’s real SingTel, StarHub, M1, and Vivifi endpoints so the IP story is correct from the first TCP handshake.

why Puppeteer hits walls without residential mobile IPs

Puppeteer controls a real Chromium instance, which gives you a big advantage over curl-based scrapers. the TLS fingerprint is correct, JavaScript executes, and browser APIs respond normally. what Puppeteer cannot fake is the network origin. when your requests arrive from an IP in an AWS ap-southeast-1 subnet or a well-known VPN provider’s range, the target’s fraud stack has already made a decision before it parses your cookies or checks your Accept-Language header. major SG e-commerce platforms, government portals, and banking apps correlate the IP’s ASN against expected user populations. a Singapore resident logging into Singpass or browsing Shopee SG does not come from a DigitalOcean Frankfurt node.

the specific failure modes Puppeteer operators run into are worth naming. rate limiting is the most common: targets allow a handful of requests per IP per hour, which is fine for a human but fatal for a scrape job that needs to hit 500 product pages. the second is soft blocking, where the site appears to serve content but quietly replaces real data with honeypot values or empty arrays. this is particularly nasty because your script returns exit code 0 and you only notice the problem when you inspect the downstream data. the third mode is the hardest to debug: Cloudflare or a similar WAF issues a browser challenge that Puppeteer handles, but subsequent requests from the same IP are silently downgraded to a CAPTCHA flow because the IP has been flagged at the challenge-response layer.

mobile IPs solve this. carriers like SingTel and StarHub operate their mobile subscriber ranges completely separately from datacenter infrastructure. the ASN ownership, PTR records, and geolocation data all point to a legitimate ISP serving real subscribers. when you understand what a mobile proxy is at the infrastructure level, the reason for the price premium over datacenter proxies becomes obvious: the modem is a real physical device on a real SIM, and the IP is allocated by the carrier the same way it would be for any prepaid subscriber.

setting up SMP credentials in Puppeteer

Singapore Mobile Proxy uses a standard ip:port:username:password credential format. you get a proxy host address, a port (either an HTTP or SOCKS5 port), and a username/password pair tied to your plan. Puppeteer’s --proxy-server launch flag handles the host and port, and you authenticate per-page using page.authenticate().

here is a minimal working setup:

import puppeteer from 'puppeteer';

const SMP_HOST = 'your-smp-endpoint.singaporemobileproxy.com';
const SMP_PORT = 8080; // HTTP port from your SMP dashboard
const SMP_USER = 'your_username';
const SMP_PASS = 'your_password';

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    args: [
      `--proxy-server=http://${SMP_HOST}:${SMP_PORT}`,
      '--no-sandbox',
      '--disable-setuid-sandbox',
    ],
  });

  const page = await browser.newPage();

  // Authenticate on every new page before any navigation
  await page.authenticate({
    username: SMP_USER,
    password: SMP_PASS,
  });

  await page.goto('https://geo.bitsofcode.com/', { waitUntil: 'networkidle2' });

  const body = await page.$eval('body', el => el.innerText);
  console.log(body); // should show a SG carrier IP

  await browser.close();
})();

a few things to get right from the start. page.authenticate() must be called before page.goto() on each new page object, including pages opened by browser.newPage(). if you open a page and navigate before calling authenticate(), the proxy will reject the connection and you will get a net error rather than a graceful auth failure. if you are using SOCKS5 instead of HTTP (see the tradeoffs in HTTP vs SOCKS5 mobile proxies), change the --proxy-server flag value to socks5:// and note that SOCKS5 credentials cannot be passed via page.authenticate() in Chromium. for SOCKS5 you need to embed credentials directly in the URI using the --proxy-server=socks5://user:pass@host:port format, though be aware this exposes the password in the process list on Linux. for most Puppeteer use cases, HTTP proxy authentication via page.authenticate() is the cleaner path.

rotating IPs per request or per session

the choice between rotating and sticky sessions is a workflow question, not a product feature question. rotating means each new connection (or each request, depending on how SMP’s endpoint is configured) gets a fresh IP from the modem pool. sticky sessions pin you to the same IP for a defined duration, typically between 1 and 30 minutes depending on your plan settings.

use rotating when your task is inherently stateless: fetching product prices, scraping search result pages, or running parallel checks where each page load is independent. use sticky sessions when your task has state: logging into an account, maintaining a shopping cart, completing a checkout flow, or anything that sets a session cookie the server validates against the originating IP. servers that bind sessions to IPs will log you out or throw a security challenge the moment the IP changes mid-session.

here is a pattern for rotating the proxy connection between browser instances rather than between pages, which is the most reliable approach because it avoids mid-session IP changes within a single browser context:

import puppeteer from 'puppeteer';

const PROXY_LIST = [
  { host: 'rotate1.singaporemobileproxy.com', port: 8080 },
  { host: 'rotate2.singaporemobileproxy.com', port: 8080 },
];
const SMP_USER = 'your_username';
const SMP_PASS = 'your_password';

async function scrapeWithProxy(proxy, url) {
  const browser = await puppeteer.launch({
    headless: true,
    args: [
      `--proxy-server=http://${proxy.host}:${proxy.port}`,
      '--no-sandbox',
    ],
  });

  try {
    const page = await browser.newPage();
    await page.authenticate({ username: SMP_USER, password: SMP_PASS });
    await page.goto(url, { waitUntil: 'domcontentloaded' });
    return await page.$eval('body', el => el.innerText);
  } finally {
    await browser.close();
  }
}

const urls = [
  'https://www.lazada.sg/products/some-item',
  'https://www.shopee.sg/products/another-item',
];

for (let i = 0; i < urls.length; i++) {
  const proxy = PROXY_LIST[i % PROXY_LIST.length];
  const result = await scrapeWithProxy(proxy, urls[i]);
  console.log(result.slice(0, 200));
}

SMP provides distinct endpoint addresses or port ranges for rotating versus sticky sessions. your dashboard will show the correct host/port combination for each mode. do not try to build rotation logic yourself by hammering a sticky endpoint rapidly, that just gets the session token invalidated and does not give you a new IP.

three real workflows where this combo wins

scraping Shopee SG and Lazada SG product data

both platforms geo-fence their SG storefronts aggressively. if your requests originate from outside Singapore, you either get redirected to a generic SEA landing page or see different pricing than what SG subscribers see. even residential IPs from neighbouring countries like Malaysia or Indonesia return different catalog data. a real SingTel or StarHub IP puts you inside the same network segment as the actual SG customer base, which means you see the same prices, promotions, and stock availability that a local user sees. this matters a lot for price intelligence work where a 5% price difference between what you scrape and what the local customer sees invalidates your entire dataset.

testing geo-fenced features in SG fintech apps

banks and fintech apps like DBS PayLah!, GrabPay, and various MAS-licensed platforms serve different UI flows depending on whether you are accessing from a local carrier IP. IP-based geo-fencing is a compliance requirement for some features, not just a performance optimization. QA teams automating regression tests for these flows need to run Puppeteer from an IP that the app’s backend classifies as domestic. a VPN or a datacenter IP will not pass this check even if it resolves to a SG geolocation in MaxMind’s database, because the apps are checking ASN ownership, not just the country code.

running SEO audits for SG SERPs

Google’s search results are personalized by country, carrier, and device type. a search from a mobile SG carrier IP returns different local pack results, different featured snippets, and different ad placements than the same query run from a datacenter IP or a US residential pool. for mobile proxies for SEO research, the carrier classification matters because Google separates its mobile index from desktop and serves results accordingly. Puppeteer running on a SG mobile carrier IP gives you the closest approximation to what a SingTel subscriber sees when they search on their phone, which is the ground truth you need for accurate rank tracking.

common pitfalls

  • user-agent and IP class mismatch: if Puppeteer sends a desktop User-Agent string but the IP is classified as a mobile carrier, some platforms will flag the inconsistency. set page.setUserAgent() to a current Android Chrome UA string that matches a realistic mobile subscriber. Puppeteer’s default headless UA is neither desktop nor mobile in a clean way.

  • rotating IPs mid-session: opening a new page in the same browser context after your proxy rotates will break session cookies that are bound to the previous IP. close the browser and open a fresh instance when you need a new IP for a new session.

  • ignoring proxy authentication errors: a failed page.authenticate() often manifests as a 407 response that Puppeteer’s page.goto() does not throw on by default. add a response listener on the first navigation to verify you are actually getting through the proxy.

  • not accounting for SG timezone in time-sensitive scrapes: some SG platforms display different prices or availability windows during off-peak hours (late night SGT). if you are running Puppeteer jobs on a UTC server, your cron schedule may be hitting the wrong time window. the IP being SG does not automatically align the server clock.

  • hardcoding the proxy host when endpoints can change: proxy endpoint addresses can be updated when infrastructure changes. read the host and port from environment variables rather than embedding them in source code so you can swap credentials without a deploy.

  • skipping waitUntil: 'networkidle2' on SPAs: single-page applications common in SG e-commerce (Shopee, Carousell) fire their critical data fetches after the initial page load. if you grab the DOM on load, you often get the skeleton UI before the product data arrives. use networkidle2 or explicit waitForSelector calls targeting the data elements you actually need.

when Singapore IPs specifically matter

not all residential mobile proxies are equivalent for SG-targeted work. US or EU residential pools give you a real-looking IP but the wrong country classification, and most SG platforms have explicit IP allow/block logic at the country level. within Southeast Asia, even a Malaysian or Indonesian mobile IP will land you in a different geo bucket on platforms like Shopee, which operates separate regional storefronts with independent pricing and catalog systems. the specificity of the carrier matters too: SingTel, StarHub, M1, and Vivifi are the four major SG mobile operators, and some platforms’ fraud systems weight trust scores differently by ASN. an IP that resolves to a known SG carrier is treated differently from a generic “SG datacenter” IP even when both carry a SG country code in the geolocation database.

there is also a practical latency benefit. running Puppeteer on a server in ap-southeast-1 with a SG mobile proxy means your requests originate from a SG carrier IP and the round-trip to SG-hosted services stays short. for automation that needs to complete within session timeout windows or that is racing against countdown timers (flash sales, ticket drops), cutting out the cross-continental latency leg that a US residential proxy would introduce is meaningful.

getting started

if you are ready to test the integration, the Singapore Mobile Proxy plans page has the current endpoint details and plan tiers, including options for sticky-only, rotating-only, and hybrid access. for most Puppeteer workflows, starting with a small rotating plan and a separate sticky allocation for logged-in session work is the practical combination. the setup time from purchasing a plan to having a working page.authenticate() call is under ten minutes, and the credential format is straightforward enough that you can drop it directly into the code patterns above without adapting anything else.

ready to try Singapore mobile proxies?

2-hour free trial. no credit card required.

start free trial
message me on telegram