Enterprise-Grade Proxy Servers: Scalable Solutions for Premium Performance
30M IPs from 195+ global locations
Extremely stable proxies - 99.7% uptime
Custom plan available to fit your needs
Contact Us
Webshare.io will process your data to manage your inquiry and inform you about our services. For more information, visit our Privacy Policy.
Thank you! Our Account Executive will get back to you within 24h
Oops! Something went wrong while submitting the form.
Get 10 free Webshare proxies
no credit card required
Try now
Blog
/
How to Use Webshare Proxies with Crawlee (Node.js & Python)
Updated on
September 4, 2026
Proxy Integration

How to Use Webshare Proxies with Crawlee (Node.js & Python)

Get 10 free Webshare proxies

no credit card required
Try now
Table of Contents

Crawlee handles the crawling logic — queues, retries, session pools, browser automation — but it doesn't solve the one thing that gets scrapers blocked fastest: sending every request from the same IP address. That's what a proxy layer is for, and Crawlee's ProxyConfiguration class makes wiring one in straightforward, whether you're on the Node.js/TypeScript version or Crawlee for Python.

This guide covers both. Pick the language section you need, or read both if your team runs a mixed stack.

Why Crawlee needs a proxy

Crawlee's session pool, retry logic, and autoscaling all assume you'll eventually get blocked and are built to recover gracefully — but none of that helps if every retry comes from the same IP. A proxy layer gives Crawlee:

  • A rotating pool of IPs so rate limits and IP-based blocks don't stop a crawl after a few hundred requests
  • Sticky sessions so a multi-step flow (login, then browse, then checkout) looks like one consistent visitor instead of a different IP on every request
  • Geo-targeting so you can crawl a site the way a visitor from a specific country would see it — pricing pages, localized search results, region-locked content

Choosing the right Webshare proxy type

Webshare offers three proxy products, and which one fits depends on what your Crawlee crawler is actually doing:

Choosing the Right Webshare Proxy Type
Use case Proxy type Why
High-volume crawling, speed matters more than stealth Proxy Server (Datacenter) Fastest, cheapest per request
Multi-step flows needing a consistent identity (login sessions, cart flows) Static Residential (ISP) Same IP persists across the whole session
Scraping sites with aggressive anti-bot detection Rotating Residential Real ISP-assigned IPs, rotates automatically

If you're not sure, start with datacenter proxies — they're the cheapest way to confirm your Crawlee setup works, and you can upgrade specific crawlers to residential once you see where you're getting blocked.

Getting your Webshare proxy credentials

  1. Sign up at Webshare and open your dashboard — new accounts start with 10 free proxies, enough to test this setup before committing to a plan.
  2. Go to Proxy List and set Connection Method to Backbone Connection. This matters: the default Direct Connection view shows credentials in a different format that won't work with the examples below.
  3. Note your username, password, and the proxy address — Backbone connections always use p.webshare.io, on port 80 (username/password auth also supports 1080, 3128, and 9999–19999).

That's the only setup needed before touching Crawlee.

Node.js / TypeScript

Install

npm install crawlee

Basic setup

ProxyConfiguration plugs into every crawler type — CheerioCrawler, HttpCrawler, JSDOMCrawler, PlaywrightCrawler, and PuppeteerCrawler — the same way:

import { CheerioCrawler, ProxyConfiguration } from 'crawlee';

const proxyConfiguration = new ProxyConfiguration({
    proxyUrls: [
        'http://username:password@p.webshare.io:80',
    ],
});

const crawler = new CheerioCrawler({
    proxyConfiguration,
    async requestHandler({ request, $, log, proxyInfo }) {
        const title = $('title').text();
        log.info(`${request.url} -> "${title}" (via ${proxyInfo?.url})`);
    },
});

await crawler.run(['https://example.com']);

proxyInfo in the request handler confirms which proxy served each request — useful for debugging before you scale up.

Sticky sessions with country targeting

Webshare supports appending parameters to your username to target a country or pin a session: {username}-{country_code}-{session_id}. Combine that with Crawlee's newUrlFunction, which receives the session ID Crawlee is currently using, and every request tied to that session gets the same Webshare IP:

import { PlaywrightCrawler, ProxyConfiguration } from 'crawlee';

const proxyConfiguration = new ProxyConfiguration({
    newUrlFunction: (sessionId) => {
        const sid = sessionId ?? Math.floor(Math.random() * 1_000_000);
        return `http://username-us-${sid}:password@p.webshare.io:80`;
    },
});

const crawler = new PlaywrightCrawler({
    proxyConfiguration,
    useSessionPool: true,
    sessionPoolOptions: { maxPoolSize: 50 },
    async requestHandler({ page, request, proxyInfo, log }) {
        await page.waitForSelector('body');
        log.info(`${request.url} via ${proxyInfo?.url}`);
    },
});

await crawler.run(['https://example.com']);

If Crawlee detects blocking, it automatically rotates to a new session ID — which, via newUrlFunction, rotates you to a new Webshare IP without any extra code.

Tiered proxies: cheap by default, escalate on blocking

tieredProxyUrls lets Crawlee start on your cheapest proxy type and automatically climb to a pricier tier only when it detects blocking on a given domain — a natural fit for mixing Webshare's three proxy types in one crawler:

const proxyConfiguration = new ProxyConfiguration({
    tieredProxyUrls: [
        ['http://dc-username:password@p.webshare.io:80'],        // Tier 0: datacenter
        ['http://isp-username:password@p.webshare.io:80'],       // Tier 1: static residential
        ['http://rotating-username:password@p.webshare.io:80'],  // Tier 2: rotating residential
    ],
});

Crawlee periodically re-checks lower tiers, so if a site's blocking eases up, it'll drop back down to the cheaper proxy automatically.

Python

Install

pip install crawlee
# Add extras depending on the crawler you need:
pip install 'crawlee[beautifulsoup]'
pip install 'crawlee[playwright]'

Basic setup

The same ProxyConfiguration class works across BeautifulSoupCrawler, HttpCrawler, and PlaywrightCrawler:

import asyncio
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawler, BeautifulSoupCrawlingContext
from crawlee.proxy_configuration import ProxyConfiguration

async def main() -> None:
    proxy_configuration = ProxyConfiguration(
        proxy_urls=[
            'http://username:password@p.webshare.io:80',
        ]
    )

    crawler = BeautifulSoupCrawler(proxy_configuration=proxy_configuration)

    @crawler.router.default_handler
    async def request_handler(context: BeautifulSoupCrawlingContext) -> None:
        title = context.soup.title.string if context.soup.title else None
        context.log.info(f'{context.request.url} -> "{title}"')

    await crawler.run(['https://example.com'])

if __name__ == '__main__':
    asyncio.run(main())

Sticky sessions with country targeting

Same pairing as the JS version: Webshare's {username}-{country_code}-{session_id} format plugged into Crawlee's new_url_function, which receives the active session_id:

import asyncio
from crawlee.playwright_crawler import PlaywrightCrawler, PlaywrightCrawlingContext
from crawlee.proxy_configuration import ProxyConfiguration

def get_webshare_url(session_id: str | None = None, request=None, proxy_tier=None) -> str:
    sid = session_id or 'default'
    return f'http://username-us-{sid}:password@p.webshare.io:80'

async def main() -> None:
    proxy_configuration = ProxyConfiguration(new_url_function=get_webshare_url)

    crawler = PlaywrightCrawler(proxy_configuration=proxy_configuration)

    @crawler.router.default_handler
    async def request_handler(context: PlaywrightCrawlingContext) -> None:
        title = await context.page.title()
        context.log.info(f'{context.request.url} -> "{title}"')

    await crawler.run(['https://example.com'])

if __name__ == '__main__':
    asyncio.run(main())

Tiered proxies

proxy_configuration = ProxyConfiguration(
    tiered_proxy_urls=[
        ['http://dc-username:password@p.webshare.io:80'],
        ['http://isp-username:password@p.webshare.io:80'],
        ['http://rotating-username:password@p.webshare.io:80'],
    ]
)

A gotcha worth knowing: the port 80/443 bug

If you're on an older version of crawlee-python, watch for a proxy URL silently turning into something like p.webshare.io:None instead of :80. This was a real bug reported against crawlee-python — a proxy on port 80 or 443 got mangled because of how the underlying HTTP client read "default" ports as None rather than the actual number. It's since been fixed, but if you see a stray :None in a proxy error, upgrading your crawlee/crawlee-python version is the fix, not your Webshare credentials.

Verifying your setup

Before pointing Crawlee at a real target, confirm the proxy itself works:

curl -x http://username:password@p.webshare.io:80 https://ipinfo.io

If that returns an IP that isn't your own, your credentials are good — any connection issue after that is in the Crawlee config, not the proxy.

Get 10 free Webshare proxies
no credit card required
Try now

Frequently Asked Questions

ProxyConfiguration accepts any proxy URL scheme your crawler's underlying client supports. Webshare provides both HTTP and SOCKS5 endpoints — for browser-based crawlers (PlaywrightCrawler, PuppeteerCrawler), HTTP is the more broadly tested path; SOCKS5 works best with HttpCrawler or direct HTTP-based crawlers.
This is some text inside of a div block.

Yes — ProxyConfiguration is created independently per crawler instance, so nothing stops you from giving a PlaywrightCrawler rotating residential proxies while a CheerioCrawler in the same codebase uses cheaper datacenter proxies.
This is some text inside of a div block.

Usually because proxyUrls (a static list) is being used instead of newUrlFunction. A static list just rotates round-robin — it has no concept of session ID. Sticky sessions require newUrlFunction/new_url_function so the session ID can be encoded into the Webshare username on each call.
This is some text inside of a div block.

No. Datacenter proxies are faster and cheaper, and plenty of targets don't do aggressive IP-based detection. Reach for residential (static or rotating) specifically when you're seeing CAPTCHAs or blocks that persist even after rotating datacenter IPs.
This is some text inside of a div block.

Yes — proxy configuration is independent of queue management and autoscaling. Nothing about adding a ProxyConfiguration changes how Crawlee schedules requests or scales concurrency.
This is some text inside of a div block.