automiqdata

How to Scrape a Website With Python Without Getting Blocked

SA By Saif Ali ·

Some links below are affiliate links — if you buy through them we may earn a commission at no extra cost to you. We only recommend tools we actually use on client work.

Almost every “why am I getting blocked?” question comes down to the same thing: your scraper looks like a scraper. Real browsers send certain headers, wait between clicks, and come from residential IPs. Bots blast requests from a datacenter with a Python user-agent. Close that gap and most sites stop caring.

Here’s the order I actually work through, cheapest fix first.

1. Send real headers

The number-one giveaway is a missing or default user-agent. Send a real one, plus the headers a browser would:

import requests

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/125.0 Safari/537.36",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}

r = requests.get("https://example.com", headers=headers, timeout=15)
print(r.status_code)

This alone fixes a surprising number of “403” problems.

2. Slow down and jitter your requests

Humans don’t fire 50 requests a second. Add a delay, and randomize it so the pattern isn’t robotic:

import time, random

for url in urls:
    scrape(url)
    time.sleep(random.uniform(1.5, 4.0))  # be a good guest

Rate-limiting isn’t just politeness — it’s the single best way to keep your IPs alive.

3. Rotate IPs with proxies

Once a site rate-limits per IP, no amount of waiting on a single IP will help. You need to spread requests across many residential IPs. Wiring a rotating proxy into requests is one line:

proxies = {
    "http": "http://user:pass@gate.provider.com:7000",
    "https": "http://user:pass@gate.provider.com:7000",
}
r = requests.get(url, headers=headers, proxies=proxies, timeout=20)

For which provider to use, I broke down the real options in Best Residential Proxies for Web Scraping — for a first project, IPRoyal’s pay-as-you-go is the easiest on-ramp, and Decodo is the best all-round upgrade.

4. Reach for a headless browser only when you must

If the data is rendered by JavaScript, requests sees an empty shell. That’s when you use a headless browser like Playwright:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(user_agent=headers["User-Agent"])
    page.goto(url, wait_until="networkidle")
    html = page.content()
    browser.close()

But headless browsers are slow and expensive — they use far more bandwidth and CPU. Before you reach for one, open your browser’s Network tab and look for the underlying JSON/API call. Nine times out of ten the site is fetching data from an endpoint you can hit directly with requests — faster, cheaper, and easier to keep unblocked.

5. Handle blocks gracefully

You will still get blocked sometimes. Detect it, back off, and retry with a fresh IP instead of crashing:

def get(url, tries=3):
    for i in range(tries):
        r = requests.get(url, headers=headers, proxies=proxies, timeout=20)
        if r.status_code == 200:
            return r
        time.sleep(2 ** i)  # exponential backoff
    return None

The stack that survives production

Put together, a scraper that doesn’t get blocked looks like: real headers + randomized delays + rotating residential proxies + the right tool (requests over headless whenever possible) + graceful retries. Nail those five and you’ll get through the vast majority of sites.

The part people underestimate is everything after it works once: keeping it running when the site changes its markup, landing clean deduplicated data somewhere useful, and not waking up to a broken pipeline. That’s the real job.


If you’d rather have a scraper built, hardened, and maintained so you never think about blocks again, send us the details — it’s what we do all day.

Rather skip the build?

I do this every day

Scrapers, pipelines, and automations — built clean, delivered on schedule, and maintained. Tell me what you need and I'll map it out.

Start a project →