JavaScript Instructions enable you to interact with web pages dynamically by automating user actions like clicking buttons, filling forms, waiting for content to load, and executing custom JavaScript code. This powerful feature allows you to scrape content that requires user interaction or appears after specific actions are performed. These instructions are essential for modern web scraping because many websites load content dynamically, hide information behind user interactions, or require form submissions to access data. With JavaScript Instructions, you can automate these interactions to extract the complete content you need.
JavaScript Instructions require js_render=true to function, as they operate within the browser environment during page rendering.

How JavaScript Instructions work

JavaScript Instructions execute sequentially within a real browser environment, simulating genuine user interactions. Each instruction waits for the previous one to complete before executing, ensuring reliable automation of complex user workflows. The process works as follows:
  1. Page loads - The browser renders the initial page content
  2. Wait/Wait_for parameters execute - Global wait conditions are processed first
  3. JavaScript Instructions execute - Each instruction runs in order, waiting for completion
  4. DOM updates - The page responds to interactions, potentially loading new content
  5. Final capture - The complete rendered page is captured after all instructions finish
This sequential execution ensures that dynamic content has time to load and that user interactions trigger the expected page changes.
JavaScript Instructions have a 40-second timeout limit. If your instruction sequence takes longer than 40 seconds to complete (due to many interactions, slow page responses, or redirects), the execution will stop and return whatever content is currently visible on the page. For complex workflows that may exceed this limit, consider using our Scraping Browser which provides up to 15 minutes of execution time and more advanced automation capabilities.

Basic usage

JavaScript Instructions are provided as a JSON array where each instruction is an object specifying the action and its parameters:
# pip install requests
import requests

url = 'https://www.example.com'
apikey = 'YOUR_ZENROWS_API_KEY'
params = {
    'url': url,
    'apikey': apikey,
    'js_render': 'true',
    'js_instructions': """[{"click":".button-selector"},{"wait":500}]""",
}
response = requests.get('https://api.zenrows.com/v1/', params=params)
print(response.text)
Use the ZenRows Builder to create and test your JavaScript Instructions visually, or use an Online URL Encoder to properly encode your instructions for API requests.

Available instructions

Click interactions

Simulates clicking on page elements like buttons, links, or interactive areas. This is one of the most fundamental interactions for web automation, enabling you to trigger actions that reveal hidden content, navigate through interfaces, or activate dynamic functionality.
JSON
[
    {"click": ".read-more-button"},
    {"click": "#submit-btn"},
    {"click": "button[data-action='load-more']"}
]
Common use cases
  • Expanding collapsed content sections to access full article text
  • Navigating through pagination to scrape multiple pages of results
  • Triggering modal dialogs or popups that contain additional information
  • Activating dropdown menus to access navigation options
  • Loading additional content dynamically (infinite scroll triggers)
  • Accepting cookie consent banners or terms of service
Best practices
  • Always wait for elements to be clickable before clicking
  • Use specific selectors to avoid clicking wrong elements
  • Combine with wait_for to handle dynamic content loading
    JSON
    # Example: Click through pagination to scrape multiple pages
    
    [
        {"wait_for": ".product-list"},           # Wait for initial content
        {"click": ".pagination .next-page"},     # Click next page
        {"wait_for": ".product-list"},           # Wait for new content to load
        {"click": ".pagination .next-page"},     # Click next page again
        {"wait": 1000}                           # Final wait for stability
    ]
    

Wait for selector

Pauses execution until a specific element appears in the DOM. This instruction is crucial for handling asynchronous content loading, which is common in modern web applications that use AJAX, React, Vue, or other dynamic frameworks.
JSON
[
    {"wait_for": ".dynamic-content"},
    {"wait_for": "#ajax-loaded-section"},
    {"wait_for": "[data-loaded='true']"}
]
When to use
  • Content loads asynchronously via AJAX or fetch requests
  • Elements appear after animations or CSS transitions
  • Waiting for user-triggered content to become available
  • Ensuring forms are fully rendered before interaction
  • After clicking buttons that trigger content loading
  • When dealing with single-page applications (SPAs)
If the selector doesn’t appear within the timeout period (default: 10 seconds), the instruction will fail and execution will continue to the next instruction. This contributes to the overall 40-second timeout limit for all JavaScript Instructions.

Wait for specific duration

Pauses execution for a fixed amount of time in milliseconds. While less precise than wait_for, this instruction is useful when you need to accommodate processes that don’t have visible indicators or when you need to ensure stability after rapid interactions.
JSON
[
    {"wait": 1000},    # Wait 1 second
    {"wait": 5000},    # Wait 5 seconds
    {"wait": 500}      # Wait 0.5 seconds
]
Use cases
  • Allowing animations to complete before taking screenshots
  • Giving time for slow-loading content when no loading indicator exists
  • Preventing rate limiting by spacing requests appropriately
  • Ensuring page stability after rapid-fire interactions
  • Waiting for third-party widgets or ads to load
  • Accommodating server processing time for form submissions
JSON
# Example: Handle slow-loading content with strategic waits

[
    {"click": ".load-data-button"},    # Trigger data loading
    {"wait": 3000},                    # Wait for server response
    {"wait_for": ".data-table"},       # Wait for table to appear
    {"wait": 1000}                     # Additional stability wait
]
The maximum allowed wait time for a single wait instruction is 10 seconds, and the total combined duration of all wait instructions cannot exceed 30 seconds. For example, you could use three {"wait": 10000} instructions (10 seconds each) but not four. This 30-second limit contributes to the overall 40-second timeout for the entire JavaScript Instructions sequence.

Wait for browser events

Waits for specific browser events to occur, providing more sophisticated timing control based on actual browser state rather than arbitrary time delays. This is particularly useful for ensuring that all network activity has completed or that the page has reached a stable state.
JSON
[
    {"wait_event": "networkidle"},        # Wait until network is idle
    {"wait_event": "networkalmostidle"},  # Wait until network is almost idle
    {"wait_event": "load"},               # Wait for page load event
    {"wait_event": "domcontentloaded"}    # Wait for DOM to be ready
]
Event descriptions
  • networkidle - No network requests for 500ms (ideal for SPAs with API calls)
  • networkalmostidle - No more than 2 network requests for 500ms (less strict timing)
  • load - Page load event fired (all resources including images loaded)
  • domcontentloaded - DOM parsing completed (faster than load event)
Use cases
  • Ensure all API calls and resource loading have completed
  • Wait for specific browser lifecycle events
  • Avoid unnecessary waiting by responding to actual browser state
  • Use browser events as more accurate timing signals than fixed delays

Fill input fields

Populates form fields with specified values, enabling automation of data entry tasks. This instruction is essential for logging into websites, submitting forms, or providing input data that triggers dynamic content loading.
JSON
[
    {"fill": ["#username", "john_doe"]},
    {"fill": ["input[name='email']", "user@example.com"]},
    {"fill": [".search-box", "search query"]}
]
Supported input types
  • Text inputs (single-line text fields)
  • Email fields (with built-in validation)
  • Password fields (securely handled)
  • Search boxes (often trigger autocomplete)
  • Textarea elements (multi-line text)
  • Number inputs (numeric data entry)
Common applications
  • Login automation for accessing protected content
  • Search form submission to find specific content
  • Filter application to narrow down results
  • Contact form completion for lead generation
  • Registration form automation for account creation
  • Configuration forms for customizing page content
JSON
# Example: Complete a contact form

[
    {"wait_for": "#contact-form"},                   # Wait for form to load
    {"fill": ["#name", "John Smith"]},               # Fill name field
    {"fill": ["#email", "john@example.com"]},        # Fill email field
    {"fill": ["#message", "Hello, I need help..."]}, # Fill message
    {"click": "#submit-button"},                     # Submit form
    {"wait": 10000}                                  # Wait 10 seconds
]

Checkbox interactions

Check or uncheck checkbox and radio button elements, allowing you to select options, agree to terms, or configure settings. These interactions are crucial for forms that require user consent or option selection.
JSON
[
    {"check": "#agree-terms"},           # Check a checkbox
    {"uncheck": "#newsletter-signup"},   # Uncheck a checkbox
    {"check": "input[name='payment'][value='credit']"}  # Select radio button
]
Use cases
  • Accepting cookie consent or privacy policies
  • Selecting payment methods or shipping options
  • Configuring notification preferences
  • Filtering product catalogs by features
  • Agreeing to terms of service during registration
  • Enabling optional services or add-ons
Using check on an already checked element will not uncheck it. Use uncheck specifically to deselect elements. This prevents accidental state changes in your automation.

Select dropdown options

Choose options from dropdown menus by their value, enabling selection from predefined lists of options. This is essential for forms that use select elements for categories, locations, or other structured data.
JSON
[
    {"select_option": ["#country-select", "USA"]},
    {"select_option": [".size-dropdown", "large"]},
    {"select_option": ["select[name='category']", "electronics"]}
]
Important notes
  • The second parameter must match the value attribute of the option, not the displayed text
  • Use the actual value from the HTML, which may differ from what users see
  • Works with both single and multiple select elements
  • Triggers change events that may load additional content
Common scenarios
  • Selecting countries for shipping calculations
  • Choosing product categories for filtered browsing
  • Setting language preferences for localized content
  • Selecting time zones or date formats
  • Choosing sorting options for search results
  • Configuring display preferences (items per page, view type)

Vertical scrolling

Scroll the page vertically by a specified number of pixels, essential for triggering content that loads based on scroll position or for ensuring elements are visible before interaction.
JSON
[
    {"scroll_y": 1000},    # Scroll down 1000 pixels
    {"scroll_y": -500},    # Scroll up 500 pixels
    {"scroll_y": 2000}     # Scroll down 2000 pixels
]
Common applications
  • Social media feeds with scroll loading
  • E-commerce sites with lazy-loaded product images
  • News sites with continuous article loading
  • Search results that load more items on scroll
  • Long-form content with progressive disclosure
  • Image galleries with scroll-triggered loading

Horizontal scrolling

Scroll the page horizontally by a specified number of pixels, useful for content that extends beyond the viewport width or for navigating horizontal carousels and galleries.
JSON
[
    {"scroll_x": 800},     # Scroll right 800 pixels
    {"scroll_x": -400},    # Scroll left 400 pixels
    {"scroll_x": 1200}     # Scroll right 1200 pixels
]
Use cases
  • Product image carousels on e-commerce sites
  • Data tables with many columns
  • Horizontal navigation menus
  • Timeline or calendar interfaces
  • Wide charts or graphs
  • Panoramic image viewers

Execute custom JavaScript (evaluate)

Run arbitrary JavaScript code within the page context, providing unlimited flexibility for complex interactions or page modifications that aren’t covered by standard instructions.
JSON
[
    {"evaluate": "document.querySelector('.modal').style.display = 'none';"},
    {"evaluate": "window.scrollTo(0, document.body.scrollHeight);"},
    {"evaluate": "document.querySelector('.load-more').scrollIntoView();"}
]
Common JavaScript patterns
JavaScript
// Scroll to specific element
"document.querySelector('.target-element').scrollIntoView();"

// Modify page styling
"document.body.style.backgroundColor = '#ffffff';"

// Trigger custom events
"document.querySelector('.button').dispatchEvent(new Event('click'));"

// Access page data
"window.dataLayer = window.dataLayer || [];"

// Remove overlays
"document.querySelectorAll('.overlay').forEach(el => el.remove());"

// Trigger infinite scroll
"window.scrollTo(0, document.body.scrollHeight);"

// Click multiple elements
"document.querySelectorAll('.expand-button').forEach(btn => btn.click());"
Advanced use cases
  • Removing cookie banners or overlay advertisements
  • Triggering complex JavaScript functions specific to the site
  • Modifying page state to reveal hidden content
  • Collecting data from JavaScript variables
  • Simulating complex user interactions
  • Bypassing client-side restrictions

CAPTCHA solving

Automatically solve CAPTCHAs using integrated solving services, enabling automation of forms and processes that are protected by CAPTCHA challenges. CAPTCHA solving requires a 2Captcha API key configured in your ZenRows Integration Settings.
JSON
[
    {"solve_captcha": {"type": "recaptcha"}},
    {"solve_captcha": {"type": "cloudflare_turnstile"}},
    {"solve_captcha": {"type": "recaptcha", "options": {"solve_inactive": true}}}
]
Supported CAPTCHA types
  • reCAPTCHA v2 - Standard checkbox and image challenges
  • reCAPTCHA v3 - Invisible background verification
  • Cloudflare Turnstile - Cloudflare’s CAPTCHA system
To ensure the CAPTCHA is solved before proceeding, add wait instructions before and after the CAPTCHA-solving step, allowing time for the CAPTCHA to load and be resolved.
[
    {"wait": 3000}, // Wait for 3 seconds to allow the page to load
    {"solve_captcha": {"type": "recaptcha"}},
    {"wait": 2000} // Wait 2 seconds to confirm CAPTCHA resolution
]

Working with iframes

Standard instructions don’t work inside iframe elements due to browser security restrictions. Iframes create isolated contexts that require special handling to access their content and interact with their elements.

Frame instructions

All frame instructions require specifying the iframe selector as the first parameter
JSON
[
    {"frame_click": ["#payment-iframe", ".submit-button"]},
    {"frame_wait_for": ["#content-iframe", ".loaded-content"]},
    {"frame_fill": ["#form-iframe", "#email", "user@example.com"]},
    {"frame_check": ["#options-iframe", "#agree-checkbox"]},
    {"frame_uncheck": ["#settings-iframe", "#notifications"]},
    {"frame_select_option": ["#dropdown-iframe", "#country", "USA"]},
    {"frame_evaluate": ["iframe-name", "document.body.style.color = 'red';"]}
]

Revealing iframe content

Extract content from iframes for processing:
JSON
[
    {"frame_reveal": "#payment-iframe"}
]
This creates a hidden div element with the iframe content encoded in base64:
HTML
<div class="iframe-content-element" style="display: none;" data-id="#payment-iframe">
    <!-- Base64 encoded iframe content -->
</div>
For security reasons, iframe content isn’t included in the standard response. Use frame_reveal to explicitly extract iframe content when needed.

Using XPath selectors

In addition to CSS selectors, you can use XPath expressions for more precise element targeting:
JSON
[
    {"click": "//button[text()='Submit']"},
    {"wait_for": "//div[@class='content' and @data-loaded='true']"},
    {"fill": ["//input[@placeholder='Enter email']", "user@example.com"]}
]
XPath advantages
  • Text-based selection - Select elements by their text content
  • Complex conditions - Use logical operators and functions
  • Hierarchical navigation - Navigate parent/child relationships easily
  • Attribute matching - Complex attribute-based selection

Debugging JavaScript Instructions

Enable detailed execution reporting by adding json_response=true to your request:
Python
params = {
    'js_render': 'true',
    'js_instructions': """[{"click":".button-selector"},{"wait":500}]""",
    'json_response': 'true'  # Enable debugging information
}

Understanding the debug report

The debug report provides comprehensive execution details:
JSON
{
    "instructions_duration": 5041,
    "instructions_executed": 4,
    "instructions_succeeded": 3,
    "instructions_failed": 1,
    "instructions": [
        {
            "instruction": "wait_for",
            "params": {"selector": ".content"},
            "success": true,
            "duration": 1200
        },
        {
            "instruction": "click",
            "params": {"selector": ".button"},
            "success": true,
            "duration": 150
        },
        {
            "instruction": "wait_for",
            "params": {"selector": ".missing-element"},
            "success": false,
            "duration": 30000
        },
        {
            "instruction": "fill",
            "params": {"selector": "#input", "value": "test"},
            "success": true,
            "duration": 80
        }
    ]
}

Common use cases and workflows

def automate_registration_form(signup_url, user_data):
"""
Complete multi-step registration process
"""
instructions = [
    # Step 1: Basic information
    {"wait_for": "#registration-form"},
    {"fill": ["#first-name", user_data['first_name']]},
    {"fill": ["#last-name", user_data['last_name']]},
    {"fill": ["#email", user_data['email']]},
    {"click": ".next-step"},
    
    # Step 2: Account details
    {"wait_for": "#account-details"},
    {"fill": ["#username", user_data['username']]},
    {"fill": ["#password", user_data['password']]},
    {"fill": ["#confirm-password", user_data['password']]},
    {"click": ".next-step"},
    
    # Step 3: Preferences
    {"wait_for": "#preferences"},
    {"check": "#newsletter-signup"},
    {"select_option": ["#country", user_data['country']]},
    {"click": ".submit-registration"},
    
    # Step 4: Confirmation
    {"wait_for": ".registration-success"},
]

return requests.get('https://api.zenrows.com/v1/', params={
    'url': signup_url,
    'apikey': 'YOUR_ZENROWS_API_KEY',
    'js_render': 'true',
    'js_instructions': json.dumps(instructions),
})

Troubleshooting

Common issues and solutions

IssueCauseSolution
Element not foundSelector doesn’t match any elementsVerify selector in browser DevTools
Click not workingElement not clickable or coveredUse wait_for to ensure element is ready
Form submission failsMissing required fieldsFill all required fields before submitting
Timeout errorsContent takes too long to loadIncrease wait times or use wait_for
Instructions skipPrevious instruction failedCheck debug report for failed instructions
Iframe content missingUsing standard instructions on iframeUse frame_* instructions for iframe elements

Debugging selector issues

When selectors aren’t working as expected:
1

Verify selector in browser

Open the target page in your browser and test the selector in DevTools console:
JavaScript
// Test CSS selector
document.querySelector('.your-selector')

// Test XPath
document.evaluate('//your/xpath', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
2

Check element timing

Ensure the element exists when the instruction runs:
JSON
[
    {"wait_for": ".parent-container"},  // Wait for parent first
    {"click": ".child-element"}         // Then interact with child
]
3

Use alternative selectors

Try different selector strategies:
JSON
[
    {"click": "#button-id"},                    // Try ID first
    {"click": "button[data-action='submit']"},  // Try attribute
    {"click": "//button[text()='Submit']"}      // Try XPath with text
]
4

Check for dynamic content

Some elements are created dynamically:
JSON
[
    {"click": ".load-content"},         // Trigger content creation
    {"wait_for": ".dynamic-element"},   // Wait for element to appear
    {"click": ".dynamic-element"}       // Then interact with it
]

Pricing

The js_instructions parameter doesn’t increase the request cost. You pay the JavaScript Render (5 times the standard price) regardless of the wait value you choose.

Frequently Asked Questions (FAQ)