Set up your scraper with ProxyField
- Test the login with one cURL request through the gateway.
- Put the proxy URL into your HTTP client or browser automation tool.
- Rotate per request for independent pages; hold a sticky session for a multi-step flow.
- Treat
403,429,503and challenge pages as failures and retry on a new IP, 2 or 3 times at most. - Start with 3-5 parallel requests per site and raise it while the success rate holds.
In Python requests the same settings are one proxy URL, used for both schemes.
import requests
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
r = requests.get("https://api.ipify.org", proxies={"http": PROXY, "https": PROXY}, timeout=30)Requests' own guide to proxies: Advanced Usage (Requests documentation).
On this page 10 sections
What you need
For the proxy type to buy for scraping and the cost per page, see web scraping proxies. For the code, you need:
- Proxy access: the gateway
gate.proxyfield.io:7000with a username and password, or your server's IP on the allowlist and the port you set up in the dashboard. Access opens once the payment is confirmed; both are in your account. - A location, if the site varies by country: written into the username, as in
<login>-country-us-city-new_york. ProxyField has 412M+ residential IPs in 195+ countries, with targeting by country, region (state) and city (proxy location targeting). - A scraper: any HTTP client or browser automation tool that accepts a proxy. The gateway speaks HTTP, HTTPS and SOCKS5; the examples below use HTTP with a username and password.
Step 1. Test the connection
Send one request through the proxy before you touch the scraper:
curl -x gate.proxyfield.io:7000 -U "<login>-country-us-city-new_york:<password>" https://api.ipify.orgThe IP it prints should not be yours. Run it twice: on a rotating gateway the 2 IPs differ. A 407 means the username or password is wrong (407 proxy authentication required).
Step 2. Put the proxy into your scraper
The proxy goes wherever your code opens connections.
Python requests
import requests
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
PROXIES = {"http": PROXY, "https": PROXY}
r = requests.get("https://example.com/products?page=1", proxies=PROXIES, timeout=30)
print(r.status_code, len(r.text))Scrapy: set the proxy per request; Scrapy's built-in proxy middleware reads the credentials from the URL.
import scrapy
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
class ProductsSpider(scrapy.Spider):
name = "products"
start_urls = ["https://example.com/products?page=1"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url, meta={"proxy": PROXY})
def parse(self, response):
for title in response.css(".product-card h2::text").getall():
yield {"title": title.strip()}Node.js (fetch with undici)
import { fetch, ProxyAgent } from "undici";
const proxy = new ProxyAgent({
uri: "http://gate.proxyfield.io:7000",
token: "Basic " + Buffer.from("<login>-country-us-city-new_york:<password>").toString("base64"),
});
const res = await fetch("https://example.com/products?page=1", { dispatcher: proxy });
console.log(res.status, (await res.text()).length);Headless browsers take the proxy at launch: see Playwright, Puppeteer and Selenium. More libraries: Python, Scrapy, Node.js, cURL.
Step 3. Decide how IPs change
| The job | Session | Why |
|---|---|---|
| Independent pages: product pages, search results, listings | A new IP on every request | Spreads the load over the pool; one block costs one request |
| A flow on one site: search → filter → page 2 → details | One sticky IP for the flow | The site sees one visitor; cookies and IP stay consistent |
| Pages behind your own login | One sticky IP for the whole session | A login that jumps between IPs looks like a stolen session |
Use logins only for your own account and for data you are allowed to access; the acceptable use policy does not allow access to data behind logins you are not entitled to. Scraping behind a login is allowed only for data you are entitled to and the site's terms allow.
ProxyField holds a sticky session from 30 seconds to 36 hours (presets 1, 5, 10 and 30 minutes, 1, 3 and 6 hours). The username syntax for a session: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default) after the location, as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600.
One detail matters for per-request rotation: an HTTPS request travels inside a tunnel through the proxy, and requests that reuse one open tunnel may leave through the exit that opened it. If every request must get a new IP, open a new connection for each one (as requests.get without a Session does); reuse a Session when speed matters more than a new IP on every request. An open HTTPS connection keeps one exit IP until it closes.
Step 4. Detect blocks and retry on a new IP
A block does not always come as an error. Count a response as failed when:
- the status is
403,429or503; - the page is a challenge or "access denied" page, even with status
200; - the content you parse is missing (no title, no price, an empty list).
Then wait and try again; on a rotating gateway the next attempt leaves from a new IP. Stop after 2 or 3 attempts and log the URL, so a hard block does not eat your traffic in a loop. Honor Retry-After when the site sends it.
import random
import re
import time
import requests
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
PROXIES = {"http": PROXY, "https": PROXY}
BLOCK_MARKERS = ("captcha", "access denied", "unusual traffic", "verify you are human")
def looks_blocked(r):
if r.status_code in (403, 429, 503):
return True
m = re.search(r"<title[^>]*>(.*?)</title>", r.text[:5000], re.I | re.S)
title = m.group(1).lower() if m else ""
return any(marker in title for marker in BLOCK_MARKERS) # check the title, not the whole page
def fetch(url, attempts=3):
for attempt in range(1, attempts + 1):
wait = 2 ** attempt
try:
r = requests.get(url, proxies=PROXIES, timeout=30)
if r.status_code == 200 and not looks_blocked(r):
return r.text
retry_after = r.headers.get("Retry-After", "")
if retry_after.isdigit():
wait = int(retry_after)
except requests.RequestException:
pass
if attempt < attempts:
time.sleep(wait + random.random()) # the next attempt gets a new IP
return None # log it and move onWhat each status code means and how to fix it: 403 forbidden, 429 too many requests, and the full proxy errors guide.
Step 5. Set the pace
Rotation spreads requests over IPs, but sites also judge the total rate against them. ProxyField allows unlimited concurrent threads, so the limit is yours to set:
- Start with 3-5 parallel requests per website.
- Raise the number while the success rate holds and response times stay flat.
- Back off at the first
429responses or when response times climb. - Add a small random delay between requests to the same site.
from concurrent.futures import ThreadPoolExecutor
urls = [f"https://example.com/products?page={n}" for n in range(1, 51)]
with ThreadPoolExecutor(max_workers=4) as pool: # 4 parallel requests to this site
pages = list(pool.map(fetch, urls))
print(sum(p is not None for p in pages), "of", len(urls), "pages fetched")Step 6. Keep the rest of the request consistent
An IP is one signal of several. Send a current browser User-Agent with the headers that browser sends (Accept, Accept-Language), keep cookies within a sticky session and drop them when the IP changes, and match the Accept-Language to the exit country where it matters. A proxy cannot fix a scraper that announces itself in its headers: what proxies can and cannot fix.
Step 7. Measure success and traffic
Log, per site: requests sent, successes (by content, not status), retries and response times. Watch traffic in the dashboard, which shows usage in real time and the gigabytes left. If traffic runs ahead of your estimate, the usual causes are a browser loading whole pages and retries in a loop: block images, fonts and media you do not parse, and cap retries at 2 or 3.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Rotating inside a login or a cart | The site sees the session jump between IPs and ends it | One sticky IP per session |
| Retrying without a limit | A blocked page eats traffic in a loop | 2-3 attempts with a pause |
| Counting status 200 as success | Challenge pages slip into your data | Check the content you parse |
| Running every thread against one site | 429 responses, then blocks | A per-site limit on parallel requests |
| Credentials in shared code | Anyone with the code can spend your traffic | Environment variables, or an IP allowlist on fixed servers |
Scrape public data, read each site's terms and robots.txt, and keep within our acceptable use policy; the legal side is in is web scraping legal.
How to Use Proxies for Web Scraping FAQ
How should retries work with proxy rotation?
ProxyField's rotating gateway gives a new IP on every request, from $1.95/GB, so a retry leaves from a new IP without extra code. Treat 403, 429, 503 and challenge pages as failures, wait a few seconds (or as long as Retry-After says), and retry. Stop after 2 or 3 attempts and log the URL instead of looping.
How many requests per IP is too many?
It depends on the site: some tolerate dozens of requests a minute from one IP, others react after a few. With per-request rotation you rarely reach any limit on one IP; what sites notice is your total rate, so cap parallel requests per site and back off at the first 429.
How do I know if a website has blocked my scraper?
Look for 403, 429 or 503 responses, challenge or "access denied" pages, and pages served with status 200 that lack the content you parse. A sudden drop in the share of pages with real content is the clearest sign.
How do you calculate the proxy concurrency you need?
Start with 3-5 parallel requests per website, raise it while the success rate holds and response times stay flat, and back off when 429 responses appear. ProxyField threads are unlimited, so the right number is set by each site, not by the proxy.
Should the IP rotate on every request when scraping with a browser?
Only when each page load is independent. A browser that moves through a site, keeps cookies or signs in should hold one sticky IP for the whole visit, because a page whose resources arrive from changing IPs looks odd to the site.
Do I need to change headers when I rotate proxies?
Keep the headers of one real browser and send them consistently; you do not need new headers for every IP. Do clear cookies when the IP changes, and keep cookies and IP together within a sticky session.
How many requests should a proxy trial include?
Enough to count: 200-500 requests per target site, sent with the headers and concurrency you plan to use, and checked for real content. At 100 KB per HTML page that is 20-50 MB, so a 1 GB trial covers several targets and countries.
When should a proxy request be retried?
Retry timeouts, connection errors, 429 and 503 responses, and pages that come back without the content you expected; wait first, and honor Retry-After when the site sends it. Do not retry 404 or other answers that will not change, and fix a 407 (wrong proxy credentials) instead of retrying it.
How do you control proxy concurrency at scale?
Set a limit of parallel requests per site rather than one global number: too many at once to one site bring 429 Too Many Requests responses, slowdowns or challenge pages, often for a while after you slow down. Rotation spreads requests over IPs but not your total rate, so raise each site's limit only while its success rate holds.