# Waiting and timing

Decide the moment the capture is taken, with wait_until, delay, wait_for_selector and timeout, and what to do when a page times out.

This request waits until the page has gone quiet, then one more second, before it captures.

```bash
curl "https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://github.com/microsoft/playwright&wait_until=networkidle&delay=1" \
  --output playwright.png
```

You get the page with its late-loading parts in place: the file list, the avatars, the README.

A page does not appear all at once. First comes the text, then styles, then images, then whatever its scripts fetch afterwards. Capture too early and you get gaps. Wait too long and every request is slow. The four options on this page let you choose.

## The four options at a glance

| Option | What it answers | Default |
| --- | --- | --- |
| [`wait_until`](https://curlshot.com/docs/options.md#wait_until) | Which loading stage counts as "loaded"? | `load` |
| [`wait_for_selector`](https://curlshot.com/docs/options.md#wait_for_selector) | Which element must be visible first? | not set |
| [`delay`](https://curlshot.com/docs/options.md#delay) | How much extra time after that? | `0` seconds |
| [`timeout`](https://curlshot.com/docs/options.md#timeout) | When do we give up? | `30` seconds |

## Choose a loading stage

### wait_until

The loading stage at which the page counts as loaded.

The default is `load`.

The allowed values are `load`, `domcontentloaded`, `networkidle` and `commit`. Here they are from earliest to latest:

| Value | In plain words | Use it when |
| --- | --- | --- |
| `commit` | The site has started to answer. The page is probably still blank. | You follow it with `wait_for_selector` and want nothing else to hold things up. |
| `domcontentloaded` | The text and structure have arrived. Images and styles may still be on their way. | The page has one slow image or ad that you do not care about. |
| `load` | The page and the files it lists, such as images and styles, have finished loading. | Most pages. This is the browser's own idea of "done". |
| `networkidle` | The `load` stage, and then the page has stopped fetching things for a moment. | Pages that build themselves with JavaScript after loading. |

`networkidle` is the slowest of the four, because it has to watch for a quiet moment.

Some pages never go quiet. They keep polling, which means asking a server for news every few seconds, or they stream data. For those, `networkidle` waits a limited time and then captures anyway. You still get a screenshot.

## Wait for one element

### wait_for_selector

Wait until an element matching this CSS selector is visible.

There is no default. Leave it out and no element is waited for.

A CSS selector is a short pattern that points at an element, such as `.chart` for an element with the class `chart`.

This is the most exact way to wait. You name the thing you need, and the capture happens as soon as it is on screen.

```bash
curl "https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://en.wikipedia.org/wiki/Eiffel_Tower&wait_for_selector=.infobox" \
  --output eiffel.png
```

The element has to be visible, not only present in the page. A hidden element does not count.

If it has not shown up when the `timeout` runs out, the request fails with [`selector_not_found`](https://curlshot.com/docs/errors.md#selector_not_found) and status `422`. Note the code: it is not `timeout`, because the page itself loaded fine.

## Add a pause

### delay

Extra time to wait after the page has loaded, in seconds.

The default is `0`. The largest value is `30`. The value is in seconds, not milliseconds. Decimals are allowed, so `0.5` is half a second.

The delay comes after `wait_until` and `wait_for_selector` are satisfied.

Use it for things no loading stage can see: an entrance animation, a chart that draws itself, a font that swaps in late.

```bash
curl "https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://developer.mozilla.org&delay=2" \
  --output mdn.png
```

A delay is a fixed cost. Two seconds of delay makes every request two seconds slower, even when the page was ready at once. Prefer `wait_for_selector` when there is an element you can name.

## Set the limit

### timeout

How long the page may take to get ready, in seconds.

The default is `30`. The smallest value is `1` and the largest is `90`.

The clock covers loading the page and waiting for `wait_for_selector`. If the page is not ready in time, the request stops with the `timeout` error.

Raise it for heavy pages. Lower it when a quick failure is more useful to you than a long wait.

```bash
curl "https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://github.com/microsoft/playwright&full_page=true&timeout=60" \
  --output playwright-full.png
```

Your own HTTP client has a timeout too. Make it longer than `timeout` plus `delay`, with some seconds to spare for the capture. Otherwise your client hangs up before the answer arrives.

## How to choose

Start with the defaults. They suit most pages. Change something only when a capture comes back wrong.

1. **Parts are missing, and there is an element you can name.** Add `wait_for_selector` for that element.
2. **Parts are missing, and you cannot name an element.** Try `wait_until=networkidle`.
3. **Still not right, or something is mid-animation.** Add a small `delay`, such as `1` or `2`. See also [`reduced_motion`](https://curlshot.com/docs/dark-mode-and-emulation.md).
4. **The request times out.** Go the other way. Use `wait_until=domcontentloaded`, block what is slow with [blocking options](https://curlshot.com/docs/blocking.md), or raise `timeout`.

> **Tip**
>
> For pages that take a long time, do not hold the connection open. Send [`async=true`](https://curlshot.com/docs/async-and-webhooks.md) and collect the result when it is done.

## The timeout error

When the page is not ready in time, you get this:

```json
{
  "error_code": "timeout",
  "error_message": "The page took too long to render. Try a larger `timeout` or a lighter `wait_until`.",
  "documentation_url": "https://curlshot.com/docs/errors#timeout"
}
```

A timed-out request is not counted against your quota.

It is safe to retry. Slow moments happen: the site may have been busy for a few seconds. If the same page times out again and again, change the request:

- Raise `timeout`, up to `90`.
- Use an earlier stage: `wait_until=load` or `wait_until=domcontentloaded`.
- Cut the weight of the page with [`block_ads`, `block_trackers`](https://curlshot.com/docs/blocking.md) or `block_resources=media`.
- Lower [`full_page_max_height`](https://curlshot.com/docs/options.md#full_page_max_height) for very long pages.

The full entry is on the [errors page](https://curlshot.com/docs/errors.md#timeout).

> **Common mistakes**
>
> - **Using `delay` for everything.** It works, but it slows every request. Try `wait_for_selector` first.
> - **Waiting for a hidden element.** `wait_for_selector` needs the element to be visible. A hidden template or a closed menu never satisfies it.
> - **Expecting `timeout` to make the capture wait longer.** It is a limit, not a pause. A page that is ready in two seconds is captured after two seconds.
> - **A client timeout shorter than the API timeout.** Your code gives up first and you never see the result or the error.
> - **An unencoded `#` in the selector.** Write `%23`, as described in [Encode the URL](https://curlshot.com/docs/screenshot-url.md#encode-the-url).

## Where to go next

- [Custom CSS, JavaScript and clicks](https://curlshot.com/docs/customize.md): change the page once it is ready.
- [Async and webhooks](https://curlshot.com/docs/async-and-webhooks.md): for renders that take a long time.
- [Errors](https://curlshot.com/docs/errors.md): what each error code means and which ones to retry.
- [Options reference](https://curlshot.com/docs/options.md#wait_until): the waiting options with their limits.
