> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zenrows.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Wait For Parameter

> Use the ZenRows wait_for parameter with CSS selectors to pause scraping until specific page elements load during JavaScript rendering.

The Wait For parameter instructs ZenRows to pause JavaScript rendering until a specific element appears on the page, identified by a CSS selector. Unlike fixed delays, Wait For provides dynamic waiting that adapts to actual page loading conditions, making your scraping more reliable and efficient.

When you specify a CSS selector with the `wait_for` parameter, ZenRows continuously monitors the page during JavaScript rendering until the target element becomes visible in the DOM. This ensures that critical content has loaded before capturing the HTML, regardless of the duration of the loading process.

<Note>The Wait For parameter requires `js_render=true` to function, as it operates within the browser environment during JavaScript rendering.</Note>

## How the Wait For parameter works

The Wait For parameter actively monitors the page's DOM structure during JavaScript rendering, checking repeatedly for the presence of your specified CSS selector. Once the target element appears and becomes visible, ZenRows immediately captures the HTML content.

This process ensures you capture:

* Elements that load at unpredictable times
* Content dependent on API response timing
* Elements that appear after user interactions
* Dynamically generated content with variable loading speeds
* Critical page components that indicate full loading completion

The monitoring continues until either the element appears or the maximum timeout of 3 minutes is reached. If the selector is not found within this timeframe, ZenRows returns a 422 error indicating that the element could not be located.

## Basic usage

Add the `wait_for` parameter with a CSS selector to your JavaScript rendering request:

<CodeGroup>
  ```python Python theme={null}
  # pip install requests
  import requests

  url = 'https://www.scrapingcourse.com/ecommerce/'
  apikey = 'YOUR_ZENROWS_API_KEY'
  params = {
      'url': url,
      'apikey': apikey,
      'js_render': 'true',
      'wait_for': '.price',
  }
  response = requests.get('https://api.zenrows.com/v1/', params=params)
  print(response.text)
  ```

  ```javascript Node.js theme={null}
  // npm install axios
  const axios = require('axios');

  const url = 'https://www.scrapingcourse.com/ecommerce/';
  const apikey = 'YOUR_ZENROWS_API_KEY';
  axios({
      url: 'https://api.zenrows.com/v1/',
      method: 'GET',
      params: {
          'url': url,
          'apikey': apikey,
          'js_render': 'true',
          'wait_for': '.price',
      },
  })
      .then(response => console.log(response.data))
      .catch(error => console.log(error));
  ```

  ```java Java theme={null}
  import org.apache.hc.client5.http.fluent.Request;

  public class APIRequest {
      public static void main(final String... args) throws Exception {
          String apiUrl = "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.scrapingcourse.com%2Fecommerce%2F&js_render=true&wait_for=.price";
          String response = Request.get(apiUrl)
                  .execute().returnContent().asString();

          System.out.println(response);
      }
  }
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, 'https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.scrapingcourse.com%2Fecommerce%2F&js_render=true&wait_for=.price');
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  $response = curl_exec($ch);
  echo $response . PHP_EOL;
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "io"
      "log"
      "net/http"
  )

  func main() {
      client := &http.Client{}
      req, err := http.NewRequest("GET", "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.scrapingcourse.com%2Fecommerce%2F&js_render=true&wait_for=.price", nil)
      if err != nil {
          log.Fatalln(err)
      }
      resp, err := client.Do(req)
      if err != nil {
          log.Fatalln(err)
      }
      defer resp.Body.Close()

      body, err := io.ReadAll(resp.Body)
      if err != nil {
          log.Fatalln(err)
      }

      log.Println(string(body))
  }
  ```

  ```ruby Ruby theme={null}
  # gem install faraday
  require 'faraday'

  url = URI.parse('https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.scrapingcourse.com%2Fecommerce%2F&js_render=true&wait_for=.price')
  conn = Faraday.new()
  conn.options.timeout = 180
  res = conn.get(url, nil, nil)
  print(res.body)
  ```

  ```bash cURL theme={null}
  curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.scrapingcourse.com%2Fecommerce%2F&js_render=true&wait_for=.price"
  ```
</CodeGroup>

This example waits until an element with the class `price` appears on the page before capturing the HTML content. The waiting time automatically adapts to the actual loading speed, up to a maximum of 3 minutes.

## CSS selector examples

Use various CSS selectors to target different types of elements:

```python Python theme={null}
# Wait for specific classes
params = {'wait_for': '.content-loaded'}

# Wait for IDs
params = {'wait_for': '#main-content'}

# Wait for specific attributes
params = {'wait_for': '[data-loaded="true"]'}

# Wait for nested elements
params = {'wait_for': '.product-container .price-display'}

# Wait for elements with specific text content
params = {'wait_for': '.status:contains("Available")'}

# Wait for form elements
params = {'wait_for': 'input[name="search"]'}

# Wait for multiple class combinations
params = {'wait_for': '.product.loaded.visible'}
```

## When to use the Wait For parameter

The Wait For parameter is essential in these scenarios:

### Dynamic content loading:

* **AJAX-loaded content** - Elements populated by asynchronous requests
* **API-dependent data** - Content that appears after external API calls
* **Progressive loading** - Pages that load content in stages
* **Conditional content** - Elements that appear based on user state or preferences
* **Real-time updates** - Content that updates based on live data feeds

### User interface elements:

* **Interactive components** - Buttons, forms, and controls that load dynamically
* **Navigation elements** - Menus and links that appear after initialization
* **Modal dialogs** - Pop-ups and overlays that appear programmatically
* **Data visualizations** - Charts, graphs, and tables that render after data loading
* **Search results** - Results that appear after query processing

## Troubleshooting

If ZenRows cannot find a matching element for the CSS selector, it will retry internally several times. If it still doesn't match after the 3-minute timeout, the request will return a 422 error. This means your selector likely does not exist in the final HTML, or is too fragile to be reliable.

### Common reasons for the 422 error when using wait\_for

### Selector Not Present in Final HTML

<Steps>
  <Step title="Inspect the site using browser DevTools">
    1. Open the page
    2. Right-click the target content and choose “Inspect”
    3. Check if your selector exists after the page fully loads
  </Step>

  <Step title="Verify your selector">
    1. Run `document.querySelectorAll('your_selector')` in the browser console
    2. If it returns no elements, your selector is incorrect

    <img src="https://static.zenrows.com/content/scrapingcourse_devtools_selector_debug_1642bd2d04.png" alt="ScrapingCourse DevTools Selector Debug" />
  </Step>

  <Step title="Tips">
    1. Use simple selectors like `.class` or `#id`
    2. Prefer stable attributes like `[data-testid="item"]`
    3. Avoid overly specific or deep descendant selectors
  </Step>
</Steps>

### Dynamic or Fragile Selectors

Some websites use auto-generated class names that change frequently. These are considered **dynamic** and unreliable.

* Re-check the page in DevTools if a previously working selector fails.
* Look for stable attributes like `data-*`.
* Use attribute-based selectors, which are more stable.

Instead of this:

```python Python theme={null}
params = {'wait_for': '.xY7zD1'} # e.g., Google Search
params = {'wait_for': '.product_list__V9tjod'} # A mix of readable and random
```

Use stable alternatives:

```python Python theme={null}
params = {'wait_for': '[data-testid="product-list"]'}
params = {'wait_for': 'img[src$=".jpg"]'}
params = {'wait_for': '[data-products="item"]'}
```

You can also combine multiple fallback selectors:

```python Python theme={null}
params = {'wait_for': '.product, .listing, [data-products="item"]'}
```

<Tip>Track your CSS selectors over time. When the target website changes its structure, you'll likely need to update your selectors.</Tip>

### Content Is Conditional or Missing

When scraping at scale, it's common to encounter pages where the expected content is missing or appears under certain conditions.

#### Common Scenarios Where Selectors Might Fail

* **Out-of-stock products**: The product is valid, but some elements like the price or “Add to cart” button are missing.

* **Deleted or unavailable pages**: You may be accessing product URLs directly, but the product has been removed. The site may return a 404 error or a custom error page without updating the URL.

* **Failed pages**: The page might fail to load properly causing your selector to not match any on the HTML.

* **Conditional rendering**: Some content is only rendered based on user location, browser behavior, or scrolling. Especially on JavaScript-heavy websites.

#### How to Handle It

Use the following ZenRows parameters to help identify these cases:

1. `original_status=true` <br />Returns the original HTTP status from the target site. Helps distinguish between a bad selector and a broken page.

   ```python Python theme={null}
   params = {
       'original_status': 'true'
   }
   ```

   <Info>For more details check the [Original Status Documentation](/universal-scraper-api/features/other#original-http-code)</Info>

2. `allowed_status_codes=404,500` <br />Lets you capture and analyze error pages instead of discarding them.

   ```python Python theme={null}
   params = {
       'allowed_status_codes': '404,500,503'
   }
   ```

   <Info>For more details check the [Allowed Status Codes Documentation](/universal-scraper-api/features/other#return-content-on-error)</Info>

3. **Best practices:**
   * Anticipate that some selectors may not match if content is missing or the page structure changes.
   * Consider checking for fallback selectors or error indicators (like a 404 message or error class).
   * Monitor your scraping jobs for unexpected increases in 422 errors, which may indicate site changes, missing data, or blocking.

### The CSS Selector Exists but Still Fails

Sometimes, your CSS selector is correct but still triggers a 422 error. Here are possible causes:

* **CSS selector is present but hidden (`display: none`)** <br />ZenRows considers it valid. If you need a visible element, consider using a child or wrapper that only appears when the content is shown.
  <Info>You can find more information about advanced CSS selectors [here](/universal-scraper-api/troubleshooting/advanced-css-selectors).</Info>
* **CSS selector appears after user interaction** <br />Use the [`js_instructions`](/universal-scraper-api/features/js-instructions) to simulate a click or scroll action first.
* **The page relies on external scripts (slow loading)** <br />Try a different `wait_for` selector that appears earlier in the loading process. Alternatively, switch to our Scraping Browser, which offers longer session times and allows you to manipulate requests more deeply through Puppeteer or Playwright.
* **CSS selector Typos:** <br />Double-check for spelling errors, missing dots (.) for classes, or missing hashes (#) for IDs.

### Alternative: Manual Wait

Instead of waiting for a selector, you can add a fixed delay using the `wait` parameter:

```python Python theme={null}
params = {'wait': 10000,} # Wait of 10 seconds
```

Useful when dynamic elements take time to appear but don't have consistent selectors. The maximum wait time is 30 seconds.

### Combine with Premium Proxy for Protected Sites

Use Wait For with Premium Proxy for maximum effectiveness:

```python Python theme={null}
params = {
    'url': url,
    'apikey': apikey,
	'js_render': 'true',
	'wait_for': '.price',
	'premium_proxy': 'true',
}
```

## Real-World Case Example

Let's say you're targeting the product grid on `scrapingcourse.com/ecommerce/`, and your request fails with a 422 error while your `wait_for` CSS selector is:

```python Python theme={null}
params = {'wait_for': '.lists-product',}
```

This selector is syntactically valid, but it does **not match any element in the HTML** of the target page, so ZenRows cannot proceed and returns a 422 error.

Inspecting the product section reveals the correct selectors:

```php HTML theme={null}
<ul class="products columns-4" id="product-list" data-testid="product-list" data-products="list">
    <li data-products="item" class="product ...">
        <a href="..." class="woocommerce-LoopProduct-link ...">
        <img src="..." class="product-image ..." />
        <h2 class="product-name woocommerce-loop-product__title">Abominable Hoodie</h2>
        <span class="price" data-testid="product-price"><span class="product-price woocommerce-Price-amount amount"><bdi><span class="woocommerce-Price-currencySymbol">$</span>69.00</bdi></span></span>
        </a>
        <a class="button product_type_variable" data-product_id="246" data-product_sku="MH09">Select options</a>
    </li>
    <!-- more li.product ... -->
</ul>
```

**Valid alternatives:**

```python Python theme={null}
params = {'wait_for': '#product-list'} # Waits for the ID 'product-list'
params = {'wait_for': '.products .product'} # Waits for any product in the product list
params = {'wait_for': '[data-products="item"]'} # Waits for any product item using a data attribute
params = {'wait_for': 'h2.product-name'} # Waits for product names
params = {'wait_for': '[data-testid="product-list"]'} # Waits for the entire product list container
```

## CSS Selector Cheat Sheet

| Selector        | Example                | Use Case                                        |
| --------------- | ---------------------- | ----------------------------------------------- |
| `.class`        | `.price`               | Wait for an elements with class “price”         |
| `#id`           | `#main-content`        | Wait for an element with ID “main-content”      |
| `[data-attr]`   | `[data-loaded="true"]` | Wait for attribute presence                     |
| `[attr^="val"]` | `[id^="item-"]`        | Attribute starts with “item-”                   |
| `[attr$="val"]` | `[src$=".png"]`        | Attribute ends with “.png”                      |
| `A > B`         | `.list > .item`        | Direct child of a parent                        |
| `A B`           | `.list .item`          | Any descendant item inside a parent             |
| `A, B`          | `.price, .discount`    | Match either `.price` or `.discount`            |
| `:nth-child(n)` | `li:nth-child(2)`      | Select the 2nd child (or any Nth) of its parent |
| `:first-child`  | `div:first-child`      | First child of a parent element                 |
| `:last-child`   | `div:last-child`       | Last child of a parent element                  |

## Pricing

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

<Tip>
  You can monitor your ZenRows usage in multiple ways to stay informed about your account activity and prevent unexpected overages.

  **Dashboard monitoring**: View real-time usage statistics, remaining requests, success rates, and request history on your [Analytics Page](https://app.zenrows.com/analytics/scraper-api). You can also set up usage alerts in your [notification settings](https://app.zenrows.com/account/notifications) to receive notifications when you approach your limits.

  **Programmatic monitoring**: For automated monitoring in your applications, call the `/v1/subscriptions/self/details` endpoint with your API key in the `X-API-Key` header. This returns real-time usage data that you can integrate into your monitoring systems. [Learn more about the usage endpoint](https://docs.zenrows.com/universal-scraper-api/features/other#plan-usage).

  **Response header monitoring**: Track your concurrency usage through response headers included with each request:

  * `Concurrency-Limit`: Your maximum concurrent requests
  * `Concurrency-Remaining`: Available concurrent request slots
  * `X-Request-Cost`: Cost of the current request
</Tip>

## Frequently Asked Questions (FAQ)

<Accordion title="Can I use XPath instead of CSS selectors?">
  Yes! Everything in this guide also applies to XPath. ZenRows fully supports XPath in the `wait_for` parameter. Please ensure that you provide a valid expression.

  Here's an example based on the HTML below:

  ```php HTML theme={null}
  <ul class="products columns-4" id="product-list" data-testid="product-list">
  <li data-products="item" class="product">
      <a href="/product/abominable-hoodie" class="woocommerce-LoopProduct-link">
      <img src="hoodie.jpg" class="product-image" />
      <h2 class="product-name">Abominable Hoodie</h2>
      <span class="price">$69.00</span>
      </a>
  </li>
  </ul>
  ```

  **Working XPath Examples:**

  ```python Python theme={null}
      params = {'wait_for': '//ul[@id="product-list"]/li'} # Waits for any <li> inside the product list.
      params = {'wait_for': '//h2[contains(@class, "product-name")]'} # Waits for a heading with a class containing product-name.
      params = {'wait_for': '//span[@class="price" and contains(text(), "69")]'} # Waits for price elements that include the number 69.
  ```

  <Tip>To test XPath in your browser, open DevTools and use the console command: `$x('//your/xpath')`.</Tip>

  Just as with CSS selectors, ensure that your XPath expressions are accurate and match the final rendered HTML. If not found, you'll still get a 422 error.
</Accordion>

<Accordion title="What happens if the element I'm waiting for never appears?">
  If the specified element doesn't appear within the 3-minute timeout limit, ZenRows will return a 422 error. This indicates that the selector could not be found in the rendered HTML. You should verify that your selector is correct and that the element exists on the page.
</Accordion>

<Accordion title="How does wait_for differ from wait when both are specified?">
  When both parameters are present, `wait_for` takes precedence and completely overrides the `wait` parameter. ZenRows will ignore the fixed timing and only wait for the specified element to appear.
</Accordion>

<Accordion title="What CSS selectors are supported?">
  ZenRows supports standard CSS selectors including classes, IDs, attributes, pseudo-classes, and complex combinations. However, some advanced CSS4 selectors or browser-specific extensions might not be supported.
</Accordion>

<Accordion title="How do I know if my selector is too specific or too general?">
  Test your selector in the browser's DevTools console using `document.querySelector('your-selector')`. A good selector should reliably match the element you want without being so specific that minor page changes break it.
</Accordion>

<Accordion title="Does wait_for work with Premium Proxy?">
  Yes, `wait_for` works perfectly with Premium Proxy. Element monitoring occurs within the browser environment, regardless of the proxy type used, although residential connections may take slightly longer to load content.
</Accordion>

<Accordion title="What's the maximum time wait_for will wait?">
  The `wait_for` parameter will wait up to 3 minutes (180 seconds) for the specified element to appear. This is the ultimate timeout limit for any ZenRows request when using the Universal Scraper API. If the element doesn't appear within this time, you'll receive a 422 error.
</Accordion>
