AlterLabAlterLab
PricingComparePlaygroundBlogDocsChangelog
    AlterLabAlterLab
    PricingComparePlaygroundBlogDocsChangelog
    IntroductionQuickstartInstallationYour First Request
    REST APIJob PollingAPI KeysSessions APINew
    OverviewPythonNode.js
    JavaScript RenderingOutput FormatsPDF & OCRCachingWebhooksJSON Schema FilteringWebSocket Real-TimeBring Your Own ProxyProAuthenticated ScrapingNewWeb CrawlingBatch ScrapingSchedulerChange DetectionCloud Storage ExportSpend LimitsOrganizations & TeamsAlerts & Notifications
    Structured ExtractionAIE-commerce ScrapingNews MonitoringPrice MonitoringMulti-Page CrawlingMonitoring DashboardAI Agent / MCPMCPData Pipeline to Cloud
    PricingRate LimitsError Codes
    From FirecrawlFrom ApifyFrom ScrapingBee / ScraperAPI
    PlaygroundPricingStatus
    Guide
    $0.004

    JavaScript Rendering

    Scrape dynamic websites that require JavaScript execution using our headless browser infrastructure.

    Cost

    JavaScript rendering uses Tier 4 (Browser). A basic JS-rendered scrape costs $0.004 per request.

    When to Use JS Rendering

    Most websites work fine with standard HTTP requests. Use JavaScript rendering only when needed:

    Use JS Rendering For:

    • Single Page Applications (React, Vue, Angular)
    • Content loaded via AJAX/fetch
    • Infinite scroll pages
    • Sites requiring user interaction simulation
    • Pages with anti-bot JavaScript checks

    Don't Need JS For:

    • Static HTML pages
    • Server-rendered content
    • APIs returning JSON
    • Most news/blog articles
    • Traditional e-commerce product pages

    Auto Mode

    Use mode: "auto" (default) and AlterLab will automatically detect if JS rendering is needed and escalate only when necessary.

    Basic Usage

    Enable JavaScript rendering by setting render_js: true in the advanced options:

    Bash
    curl -X POST https://api.alterlab.io/api/v1/scrape \
      -H "X-API-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/spa-page",
        "advanced": {
          "render_js": true
        }
      }'
    Try in Playground

    Wait Conditions

    Control when the page is considered "ready" for scraping. This is crucial for SPAs where content loads asynchronously.

    ConditionDescriptionBest For
    domcontentloadedDOM is ready, external resources may still loadFast pages, minimal JS
    loadPage and all resources fully loadedImage-heavy pages
    networkidleNo network activity for 500ms (default)SPAs, AJAX-heavy pages
    Python
    # Wait for network to be idle (default, best for SPAs)
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://react-app.example.com",
            "advanced": {
                "render_js": True,
                "wait_condition": "networkidle"
            }
        }
    )
    
    # Fast mode - don't wait for all resources
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://simple-page.example.com",
            "advanced": {
                "render_js": True,
                "wait_condition": "domcontentloaded"
            }
        }
    )

    Wait for Selector

    Wait for a specific element to appear before capturing the page. This is the most reliable way to ensure dynamic content is loaded.

    Python
    # Wait for product grid to load
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://shop.example.com/products",
            "advanced": {
                "render_js": True
            },
            "wait_for": ".product-grid"  # CSS selector
        }
    )
    
    # Wait for specific data attribute
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://app.example.com/dashboard",
            "advanced": {
                "render_js": True
            },
            "wait_for": "[data-loaded='true']"
        }
    )

    Timeout

    The selector wait has a default timeout of 30 seconds. If the element doesn't appear, the request will return with whatever content is available.

    Capturing Screenshots

    Capture a full-page screenshot along with the HTML content. Screenshots are returned as base64-encoded PNG.

    Python
    import base64
    
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://example.com",
            "advanced": {
                "render_js": True,
                "screenshot": True
            }
        }
    )
    
    data = response.json()
    
    # Save screenshot to file
    if data.get("screenshot"):
        screenshot_bytes = base64.b64decode(data["screenshot"])
        with open("screenshot.png", "wb") as f:
            f.write(screenshot_bytes)

    Cost

    Screenshots add +$0.0002 to your request.

    PDF Generation

    Generate a PDF of the rendered page. Useful for archiving or creating printable versions of web pages.

    Python
    import base64
    
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://example.com/report",
            "advanced": {
                "render_js": True,
                "generate_pdf": True
            }
        }
    )
    
    data = response.json()
    
    # Save PDF to file
    if data.get("pdf"):
        pdf_bytes = base64.b64decode(data["pdf"])
        with open("page.pdf", "wb") as f:
            f.write(pdf_bytes)

    Cost

    PDF generation adds +$0.0004 to your request.

    Single Page Apps (SPAs)

    Modern SPAs built with React, Vue, or Angular require special handling. Here's a reliable pattern:

    Python
    # Scraping a React application
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://react-store.example.com/products/123",
            "advanced": {
                "render_js": True,
                "wait_condition": "networkidle"
            },
            # Wait for the main content container
            "wait_for": "[data-testid='product-details']",
            "timeout": 60  # Allow more time for complex apps
        }
    )
    
    # Scraping a Vue application with lazy loading
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://vue-app.example.com/dashboard",
            "advanced": {
                "render_js": True,
                "wait_condition": "networkidle"
            },
            "wait_for": ".dashboard-loaded"
        }
    )

    Tips for SPAs:

    • 1. Find a reliable selector: Look for elements that only appear after data loads
    • 2. Use networkidle: This waits for all AJAX calls to complete
    • 3. Increase timeout: Complex apps may need 60-90 seconds
    • 4. Check for loading states: Wait for spinners/skeletons to disappear

    Timeout & Scroll Configuration

    Request Timeout

    Control the maximum time (in seconds) for a JS rendering request with the timeout parameter:

    ParameterRangeDefault
    timeout1 - 300 seconds90 seconds

    Scroll Configuration

    Use the enable_scroll parameter to control automatic page scrolling for lazy-loaded images and content:

    ValueBehaviorOverhead
    trueAlways scroll to capture lazy-loaded images+5-10 seconds
    falseNever scroll (faster, may miss dynamic images)None
    null (default)Auto-detect: scrolls unless the site is a social media platformVaries
    JSON
    {
      "url": "https://example.com/gallery",
      "advanced": { "render_js": true },
      "enable_scroll": true,
      "timeout": 120
    }

    Infinite Scroll Pages

    For pages with infinite scroll, you'll typically get the initial viewport content. For full content, consider using pagination or the async API with custom scroll handling.

    Python
    # Get initial content from infinite scroll page
    response = requests.post(
        "https://api.alterlab.io/api/v1/scrape",
        headers={"X-API-Key": "YOUR_API_KEY"},
        json={
            "url": "https://social-feed.example.com",
            "advanced": {
                "render_js": True,
                "wait_condition": "networkidle"
            },
            # Wait for initial items to load
            "wait_for": ".feed-item:nth-child(10)"
        }
    )
    
    # For more content, look for pagination APIs
    # Many "infinite scroll" sites have underlying REST/GraphQL APIs
    # that you can call directly without JS rendering

    Pro Tip

    Many infinite scroll sites have underlying APIs that return JSON. Use browser DevTools to find these APIs, then scrape them directly without JS rendering for better performance and lower costs.

    Cost Optimization

    JS rendering costs 4x more than basic scraping. Here's how to minimize costs:

    1. Use Auto Mode

    Set mode: "auto" and let AlterLab detect when JS is needed. We'll try cheaper methods first.

    2. Set Cost Controls

    Limit how much you're willing to spend per request:

    JSON
    {
      "url": "https://example.com",
      "cost_controls": {
        "max_tier": "3",     // Don't escalate beyond stealth
        "max_cost": 0.001,   // Cap at $0.001 per request
        "prefer_cost": true  // Optimize for lowest cost
      }
    }

    3. Cache Results

    Enable caching for pages that don't change frequently:

    JSON
    {
      "url": "https://example.com",
      "cache": true,
      "cache_ttl": 3600  // Cache for 1 hour
    }

    Troubleshooting

    Content is empty or incomplete

    • Try using wait_for with a specific selector
    • Increase the timeout value
    • Use wait_condition: "networkidle"

    Page shows "Please enable JavaScript"

    • Make sure render_js: true is set in advanced options
    • The site may have additional anti-bot measures - try using a higher tier

    Request times out

    • Increase timeout (max 300 seconds)
    • Use sync: false for long-running scrapes
    • Check if the selector in wait_for actually exists

    Getting blocked or CAPTCHAs

    • AlterLab automatically handles most anti-bot measures
    • For persistent blocks, we'll escalate to CAPTCHA-solving tier automatically
    • Consider using your own proxies with use_own_proxy: true
    Node.jsOutput Formats
    Last updated: March 2026

    On this page