Loading page content.
Loading page content.
Retail data
Catalogue, availability, ratings and seller data change hourly and are served differently to every visitor. Collecting them at catalogue scale is a routing problem: cheap exits for the easy pages, residential for the ones that fight.
The problem
At a few thousand pages a day, e-commerce collection is a scraping problem and any working setup will do. At several million, it becomes an economics problem. Page weight dominates the bill, marketplace defences dominate the success rate, and the difference between a well-routed fleet and a naive one is a factor of five on cost for the same dataset.
Marketplaces are also not one target. A single retailer runs a catalogue on one stack, a search endpoint on another, reviews behind a third, and seller pages behind a fourth, each with its own defences and its own rate tolerance. Treating them uniformly means either over-paying for residential egress on endpoints that never needed it, or getting blocked on the ones that did.
Then there is variant explosion. A product with five colours and eight sizes may be forty URLs, or one URL with a state machine behind it, and the two require completely different collection strategies. Deciding this per marketplace, rather than globally, is what keeps the crawl budget aimed at pages that carry new information.
Why proxies
The efficient pattern is a routing table keyed on hostname and path prefix, mapping each endpoint class to the cheapest pool that reliably serves it. Sitemaps, category listings and JSON endpoints usually run happily on rotating datacenter egress. Product detail pages on large marketplaces usually need residential. Very few endpoints need mobile.
Both pools share one gateway and one credential format, so routing is a string change in the proxy URL rather than a second integration. That is what makes the routing table maintainable: when a target's defences change, you move one entry, not one code path.
Recommended product
A fresh residential IP on every request, or one held for 30 minutes
from $1.20/GB
Sitemaps, category pages, image CDNs and public JSON endpoints are lightly defended and belong on the cheaper pool. On a typical fleet this is the majority of requests and a minority of the cost.
from $2.50/IP/wk
Long-lived collectors and non-HTTP protocols that need a stable dedicated exit with UDP support are better served by a SOCKS5 lease than by a rotating pool.
Worked example
The routing table is the highest-leverage thirty lines in an e-commerce collector. It decides which pool serves each request, and it is where cost is actually controlled.
import { ProxyAgent, fetch } from "undici";
const USER = "fp_8s2k4d19";
const PASSWORD = "Xk7mQ2pTz9vRn4Ls";
function proxyUrl({ pool, country }) {
// "pool-dc" selects rotating datacenter; omitting it uses residential.
const segments = [USER, "country", country];
if (pool === "datacenter") segments.push("pool", "dc");
return `http://${segments.join("-")}:${PASSWORD}@gate.fleetproxy.com:8080`;
}
// Cheapest pool that reliably serves each endpoint class. Re-measure quarterly:
// defences move in both directions and this table decays.
const ROUTES = [
{ match: /\/sitemap.*\.xml$/, pool: "datacenter" },
{ match: /\/api\/catalog\//, pool: "datacenter" },
{ match: /\/s\?k=/, pool: "residential" },
{ match: /\/dp\//, pool: "residential" },
];
const agents = new Map();
function agentFor(url, country) {
const pool = ROUTES.find((route) => route.match.test(url))?.pool ?? "residential";
const key = `${pool}:${country}`;
if (!agents.has(key)) {
agents.set(key, new ProxyAgent(proxyUrl({ pool, country })));
}
return { pool, agent: agents.get(key) };
}
export async function collect(url, country = "us") {
const { pool, agent } = agentFor(url, country);
const response = await fetch(url, {
dispatcher: agent,
headers: { "accept-language": "en-US,en;q=0.9" },
});
// Record which pool served the request. Without it you cannot tell whether a
// rising block rate is the target changing or the routing table decaying.
return { status: response.status, pool, body: await response.text() };
}Replace the credential with the one in your dashboard. The gateway host, port and username format are the same across every product.
Pitfalls
Each of these is common, cheap to fix, and expensive to leave in place. They are listed in roughly the order teams hit them.
Headless browsers pull images, fonts, analytics and third-party scripts, which commonly multiplies transfer by five to ten against a document fetch. Render only the pages whose data genuinely requires JavaScript execution, and block non-essential resource types when you do.
Most of a catalogue does not change between cycles. Use sitemap lastmod, conditional requests and change history to decide what to fetch, and spend the saved budget on the volatile fields — price, stock and buy box — at a higher cadence.
A colour and size matrix can turn one product into dozens of near-identical pages. Identify the variant axis once per marketplace, collect the canonical product plus the variant state, and skip the rest.
A conservative global limit throttles the easy hosts and still overshoots the strict ones. Rate limits belong per hostname, adapted from the observed block rate, alongside the routing decision for that host.
Questions
Start on 50MB of free residential bandwidth, measure your own targets, and scale into volume tiers that step the rate down as the job grows. Unused bandwidth never expires.
No card required for the trial. Cancel or downgrade at any time.