JavaScript Rendering
Scrape dynamic websites that require JavaScript execution using our headless browser infrastructure.
Cost
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
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:
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
}
}'Wait Conditions
Control when the page is considered "ready" for scraping. This is crucial for SPAs where content loads asynchronously.
| Condition | Description | Best For |
|---|---|---|
domcontentloaded | DOM is ready, external resources may still load | Fast pages, minimal JS |
load | Page and all resources fully loaded | Image-heavy pages |
networkidle | No network activity for 500ms (default) | SPAs, AJAX-heavy pages |
# 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.
# 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
Capturing Screenshots
Capture a full-page screenshot along with the HTML content. Screenshots are returned as base64-encoded PNG.
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
PDF Generation
Generate a PDF of the rendered page. Useful for archiving or creating printable versions of web pages.
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
Single Page Apps (SPAs)
Modern SPAs built with React, Vue, or Angular require special handling. Here's a reliable pattern:
# 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
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.
# 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 renderingPro Tip
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:
{
"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:
{
"url": "https://example.com",
"cache": true,
"cache_ttl": 3600 // Cache for 1 hour
}Troubleshooting
Content is empty or incomplete
- Try using
wait_forwith a specific selector - Increase the
timeoutvalue - Use
wait_condition: "networkidle"
Page shows "Please enable JavaScript"
- Make sure
render_js: trueis 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: falsefor long-running scrapes - Check if the selector in
wait_foractually 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