Web Scraping CorePremium tier

How to Scrape Infinite Scroll Websites

Infinite scroll looks like one page but is dozens of paginated API calls triggered by viewport geometry. Stopping after the first screen misses most of the catalog. This guide covers Playwright scroll loops, Intersection Observer instrumentation, and proxy rotation so you load every batch without tripping velocity defenses.

JSWritten by Jordan Smith·Reviewed by Alex Rivera·Updated September 4, 2026
12 min read
Editorial board contrasting the first infinite-scroll screen with a full catalog past the sentinel.

Key takeaways

  • Infinite scroll loads the next page when a sentinel element enters the viewport — you must scroll until that chain stops.
  • Fixed scroll counts fail — detect completion by stable DOM height or unchanged item count between scrolls.
  • Many sites fetch via XHR when scrolling; intercepting network responses is often cleaner than DOM parsing.
  • Scroll too fast and you outrun lazy images but under-fetch data; too slow and you waste browser minutes.
  • Route Playwright through rotating residential proxies so long scroll sessions do not stack on one IP.

On this page

What goes wrong when this scraper fails

Social feeds, marketplace grids, and job boards increasingly replace pagination with scroll-triggered loading. Your first Playwright run captures twenty items and declares victory while four hundred remain below the fold. Naive page.evaluate('window.scrollTo(0, document.body.scrollHeight)') sometimes works; often it fires before listeners attach, or the site debounces scroll and coalesces requests. The result is partial datasets that bias analytics toward recent or promoted items at the top.

Why this failure mode happens

Infinite scroll is implemented with Intersection Observer callbacks or scroll event listeners that fetch the next cursor when a sentinel div becomes visible. Headless browsers that never scroll never trigger those fetches. Even when they scroll, race conditions — scrolling before the previous XHR completes — can skip pages. Some sites virtualize the DOM and remove off-screen nodes, so item counts plateau while the cursor still advances. Understanding the site's load contract is as important as the scroll mechanics.

Pipeline finding a sentinel, scrolling, waiting for XHR, then stopping on a stable item count.
Fixed scroll counts fail. Completion is a stable DOM height or unchanged item count.

Challenges that make this hard to automate

  • Knowing when scrolling is complete vs temporarily idle
  • Virtualized lists that reuse DOM nodes and confuse naive counting
  • XHR cursors hidden in API calls rather than visible DOM
  • Rate limits triggered by dozens of fetches in one long session
  • Different mobile vs desktop scroll containers and breakpoints

Approaches that usually fail

  • Fixed repeat scroll(0, 99999) loops — stop too early or loop forever
  • Scraping only the first viewport — fast but systematically incomplete
  • Guessing page= query params that the SPA ignores
  • Single long session on one IP — velocity blocks mid-catalog
  • Parsing rendered HTML when network JSON has the full item list

When residential proxies fix this — and when they cannot

Long scroll sessions generate many sequential fetches from one browser context. Rotating residential IPs per catalog URL (or per N scroll batches on multi-listing crawls) spreads that velocity. For single deep scrolls, a sticky session keeps cookies coherent while Premium pool breadth handles multi-URL crawls across a site.

Two panes: a virtualized DOM that reuses nodes versus intercepted cursor XHR batches.
Virtualized lists reuse DOM nodes. Item counts can plateau while the cursor still advances.

How Aethyn residential proxies help here

Infinite scroll workloads are browser-heavy but not always Elite-tier targets. Premium on port 2099 balances cost with rotation breadth for catalog-scale scroll crawls.

  • Premium residential rotation (port 2099) for multi-listing crawl jobs
  • Sticky sessions when one scroll flow needs consistent cookies
  • Country targeting for geo-specific infinite feeds
  • Standard Playwright proxy config on a single host
  • Per-byte metering — intercept JSON responses to avoid downloading full image payloads

How to implement this with residential proxies

  1. 1

    Identify the load trigger: scroll sentinel vs API cursor

    Open DevTools, scroll manually, and watch Network for XHR/fetch calls. Note cursor/limit/offset parameters. If requests carry the data, intercept them in Playwright rather than scraping cards from the DOM.

    Field note: Filter Network by Fetch/XHR before scrolling — the first new request after scroll often reveals the cursor API pattern.

  2. 2

    Scroll until item count or height stabilizes

    Loop: scroll the list container to bottom, wait for network idle or a selector count increase, compare item count to previous iteration. Break when count unchanged after two passes. Prefer scrolling the overflow container element, not window, on nested feeds.

    Python (Playwright scroll loop)
    from playwright.sync_api import sync_playwright
    
    with sync_playwright() as p:
        browser = p.chromium.launch(proxy={
            "server": "http://proxy.aethyn.io:2099",
            "username": "aethyn-XXXXX-country-us",
            "password": "PASSWORD",
        })
        page = browser.new_page()
        page.goto("https://shop.example/category/shoes", wait_until="networkidle")
        prev = 0
        stable = 0
        while stable < 2:
            page.locator("[data-testid='product-card']").last.scroll_into_view_if_needed()
            page.wait_for_timeout(1500)
            count = page.locator("[data-testid='product-card']").count()
            stable = stable + 1 if count == prev else 0
            prev = count
        print("items:", prev)
        browser.close()

    Field note: Virtualized lists cap DOM node count while the cursor still advances — if count plateaus early, switch to network interception for the cursor API.

  3. 3

    Hook Intersection Observer for sentinel visibility

    Some sites only fetch when a specific sentinel enters view. Inject a small script to observe the sentinel and await a custom event, or use page.wait_for_selector on the loading spinner disappearing.

    JavaScript (in-page observer)
    await page.evaluate(() => new Promise((resolve) => {
      const sentinel = document.querySelector("[data-infinite-scroll-sentinel]");
      if (!sentinel) return resolve("no-sentinel");
      const io = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting) resolve("visible");
      }, { root: document.querySelector("main"), threshold: 0.1 });
      io.observe(sentinel);
      window.scrollTo(0, document.body.scrollHeight);
    }));

    Field note: Match root to the site's scroll container — observing against document when the list scrolls inside a div never fires.

  4. 4

    Intercept JSON responses for structured extraction

    Register page.on('response') for URLs matching the cursor API. Append items to a list as responses arrive. This avoids re-parsing the DOM on every scroll and captures fields not rendered in cards.

    Python (response handler)
    items = []
    
    def on_response(resp):
        if "/api/listing" in resp.url and resp.status == 200:
            items.extend(resp.json().get("products", []))
    
    page.on("response", on_response)
    # ... run scroll loop ...
    print(len(items))

    Field note: Deduplicate by product ID in the handler — overlapping scrolls sometimes re-fetch the previous page.

  5. 5

    Cap scroll depth and rotate between listing URLs

    Set a max scroll iterations guard to avoid infinite loops on broken sites. When crawling many category URLs, open a fresh context (or rotate proxy) per category so one marathon session does not concentrate velocity on one IP.

Best practices that keep scrapers reliable

  • Detect scroll completion by stable counts, not fixed iterations
  • Scroll the correct overflow container, not always window
  • Prefer network interception when cursor APIs exist
  • Deduplicate items in the response handler
  • Set max-iteration guards against runaway loops
  • Rotate proxies between heavy listing URLs

Common mistakes that burn proxy budget

  • Scraping only the first screen of results
  • Scrolling window when the list is in a nested div
  • No stability check — infinite loop on loading spinners
  • Ignoring virtualized DOM that caps visible nodes
  • One IP for hundreds of scroll-triggered XHRs
  • Parsing DOM when JSON responses contain fuller data

Common questions about this scraper problem

Why does my scroll loop stop at 20 items?
You likely never triggered the sentinel fetch or scrolled the wrong container. Inspect Network tab while scrolling manually to see when API calls fire.
Scroll loop or direct API calls?
Direct API calls are faster when you can reproduce cursor auth. Use scroll loops when tokens are short-lived or tied to browser session state.
How do I handle virtualized lists?
Do not rely on DOM node count alone. Intercept network responses or read cursor tokens from the API payloads.
How fast should I scroll?
Wait for network idle or selector growth between scrolls — typically 1–2 seconds. Faster scrolling outruns debounced fetch handlers.
Do I need Elite proxies for infinite scroll?
Usually Premium suffices for retail and content feeds. Use Elite if the site runs Cloudflare Bot Management and challenges during scroll fetches.
Can I scrape infinite scroll without a browser?
Only if you reverse-engineer the cursor API and reproduce required headers/cookies. Many feeds need Playwright at least once to capture tokens.
How do I avoid duplicate items?
Key on stable product IDs in the response handler and dedupe before storage. Overlapping fetches during scroll often repeat the last batch.
What about mobile infinite scroll layouts?
Set Playwright viewport and User-Agent to mobile and scroll the mobile container. Desktop and mobile feeds sometimes use different APIs.
Share this article

Solve this with Aethyn residential proxies

Route your first request through rotating residential IPs in minutes, with transparent per-byte pricing.

All solutions