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

# Use browser actions

> Click, wait, or scroll before scraping or extracting a page, then check which interactions succeeded.

Browser actions change the rendered page before Context.dev captures its content. Use them for consent dialogs, “load more” buttons, tabs, lazy-loaded feeds, and scrollable panels.

<Note>
  Actions require a paid plan. A Markdown, HTML, or image request with actions costs 2 credits. An enriched image request costs 5 credits. Actions bypass the scrape cache.
</Note>

## Prerequisites

Export an API key from the [dashboard](https://context.dev/dashboard):

```bash theme={null}
export CONTEXT_DEV_API_KEY="ctxt_secret_..."
```

## Scroll a lazy-loaded page

Pass up to five actions in execution order. This request scrolls at most ten viewports and stops early when the page no longer grows.

<CodeGroup>
  ```bash cURL theme={null}
  curl --get https://api.context.dev/v1/web/scrape/markdown \
    --header "Authorization: Bearer $CONTEXT_DEV_API_KEY" \
    --data-urlencode "url=https://example.com/feed" \
    --data-urlencode 'actions=[{"do":"scroll","direction":"down","amount":"viewport","maxScrolls":10}]'
  ```

  ```typescript TypeScript theme={null}
  import ContextDev from "context.dev";

  const client = new ContextDev({apiKey: process.env.CONTEXT_DEV_API_KEY});
  const url = "https://example.com/feed";

  const page = await client.web.webScrapeMd({url}, {
    query: {
      url,
      actions: JSON.stringify([
        {
          "do": "scroll",
          "direction": "down",
          "amount": "viewport",
          "maxScrolls": 10
        }
      ]),
    },
  });

  console.log(page.actionsApplied);
  console.log(page.markdown);
  ```

  ```python Python theme={null}
  import json
  import os
  from context.dev import ContextDev

  client = ContextDev(api_key=os.environ["CONTEXT_DEV_API_KEY"])

  page = client.web.web_scrape_md(
      url="https://example.com/feed",
      extra_query={
          "actions": json.dumps([
            {
              "do": "scroll",
              "direction": "down",
              "amount": "viewport",
              "maxScrolls": 10
            }
          ]),
      },
  )

  print(page.actions_applied)
  print(page.markdown)
  ```

  ```ruby Ruby theme={null}
  require "cgi/core"
  require "json"
  require "context_dev"

  client = ContextDev::Client.new(api_key: ENV.fetch("CONTEXT_DEV_API_KEY"))
  actions = [{do: "scroll", direction: "down", amount: "viewport", maxScrolls: 10}]

  page = client.web.web_scrape_md(
    url: "https://example.com/feed",
    request_options: {extra_query: {"actions" => [JSON.generate(actions)]}},
  )

  puts page.actions_applied
  puts page.markdown
  ```

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

  import (
      "context"
      "fmt"
      "os"

      contextdev "github.com/context-dot-dev/context-go-sdk/v2"
      "github.com/context-dot-dev/context-go-sdk/v2/option"
  )

  func main() {
      client := contextdev.NewClient(
          option.WithAPIKey(os.Getenv("CONTEXT_DEV_API_KEY")),
      )

      page, err := client.Web.WebScrapeMd(
          context.Background(),
          contextdev.WebWebScrapeMdParams{URL: "https://example.com/feed"},
          option.WithQuery("actions", `[{"do":"scroll","direction":"down","amount":"viewport","maxScrolls":10}]`),
      )
      if err != nil {
          panic(err)
      }

      fmt.Println(page.ActionsApplied)
      fmt.Println(page.Markdown)
  }
  ```

  ```php PHP theme={null}
  <?php

  require __DIR__.'/vendor/autoload.php';

  use ContextDev\Client;

  $client = new Client(apiKey: getenv('CONTEXT_DEV_API_KEY'));

  $page = $client->web->webScrapeMd(
      url: 'https://example.com/feed',
      actions: [
          ['do' => 'scroll', 'direction' => 'down', 'amount' => 'viewport', 'maxScrolls' => 10],
      ],
  );

  print_r($page->actionsApplied);
  echo $page->markdown, PHP_EOL;
  ```
</CodeGroup>

<Note>
  TypeScript, Python, Ruby, and Go SDK 2.14.0 need an explicit JSON query value for actions. The examples use request options to preserve the action array.
</Note>

## Choose an action

| Action    | Required fields | Optional controls                                | Use it for                                                       |
| --------- | --------------- | ------------------------------------------------ | ---------------------------------------------------------------- |
| `wait`    | `do`, `timeMs`  | none                                             | A known delay after another action. Maximum 30 seconds.          |
| `perform` | `do`, `action`  | none                                             | One natural-language click or interaction, up to 500 characters. |
| `scroll`  | `do`            | `direction`, `amount`, `container`, `maxScrolls` | Page or panel scrolling.                                         |

A bare `{ "do": "scroll" }` scrolls one viewport down. `amount` can be a pixel count, `viewport`, or `max`; `maxScrolls` accepts 1 to 50.

## Combine click and wait

Use a specific instruction that can be verified from visible page state, then wait only as long as the resulting update needs.

<CodeGroup>
  ```bash cURL theme={null}
  curl --get https://api.context.dev/v1/web/scrape/markdown \
    --header "Authorization: Bearer $CONTEXT_DEV_API_KEY" \
    --data-urlencode "url=https://example.com/results" \
    --data-urlencode 'actions=[{"do":"perform","action":"click the \"Load more\" button"},{"do":"wait","timeMs":1500}]'
  ```

  ```typescript TypeScript theme={null}
  import ContextDev from "context.dev";

  const client = new ContextDev({apiKey: process.env.CONTEXT_DEV_API_KEY});
  const url = "https://example.com/results";

  const page = await client.web.webScrapeMd({url}, {
    query: {
      url,
      actions: JSON.stringify([
        {
          "do": "perform",
          "action": "click the \"Load more\" button"
        },
        {
          "do": "wait",
          "timeMs": 1500
        }
      ]),
    },
  });

  console.log(page.markdown);
  ```

  ```python Python theme={null}
  import json
  import os
  from context.dev import ContextDev

  client = ContextDev(api_key=os.environ["CONTEXT_DEV_API_KEY"])

  page = client.web.web_scrape_md(
      url="https://example.com/results",
      extra_query={
          "actions": json.dumps([
            {
              "do": "perform",
              "action": "click the \"Load more\" button"
            },
            {
              "do": "wait",
              "timeMs": 1500
            }
          ]),
      },
  )

  print(page.markdown)
  ```

  ```ruby Ruby theme={null}
  require "cgi/core"
  require "json"
  require "context_dev"

  client = ContextDev::Client.new(api_key: ENV.fetch("CONTEXT_DEV_API_KEY"))
  actions = [
    {do: "perform", action: 'click the "Load more" button'},
    {do: "wait", timeMs: 1500},
  ]

  page = client.web.web_scrape_md(
    url: "https://example.com/results",
    request_options: {extra_query: {"actions" => [JSON.generate(actions)]}},
  )

  puts page.markdown
  ```

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

  import (
      "context"
      "fmt"
      "os"

      contextdev "github.com/context-dot-dev/context-go-sdk/v2"
      "github.com/context-dot-dev/context-go-sdk/v2/option"
  )

  func main() {
      client := contextdev.NewClient(
          option.WithAPIKey(os.Getenv("CONTEXT_DEV_API_KEY")),
      )

      page, err := client.Web.WebScrapeMd(
          context.Background(),
          contextdev.WebWebScrapeMdParams{URL: "https://example.com/results"},
          option.WithQuery("actions", `[{"do":"perform","action":"click the \"Load more\" button"},{"do":"wait","timeMs":1500}]`),
      )
      if err != nil {
          panic(err)
      }

      fmt.Println(page.Markdown)
  }
  ```

  ```php PHP theme={null}
  <?php

  require __DIR__.'/vendor/autoload.php';

  use ContextDev\Client;

  $client = new Client(apiKey: getenv('CONTEXT_DEV_API_KEY'));

  $page = $client->web->webScrapeMd(
      url: 'https://example.com/results',
      actions: [
          ['do' => 'perform', 'action' => 'click the "Load more" button'],
          ['do' => 'wait', 'timeMs' => 1500],
      ],
  );

  echo $page->markdown, PHP_EOL;
  ```
</CodeGroup>

For an inner panel, add a CSS selector:

```json theme={null}
{
  "do": "scroll",
  "container": "[data-results-panel]",
  "amount": "viewport",
  "maxScrolls": 5
}
```

## Verify the outcome

Do not infer success only from the HTTP status. When action outcomes are returned, inspect `actionsApplied` in request order:

```json theme={null}
{
  "actionsApplied": [
    {
      "instruction": "click the Load more button",
      "status": "applied",
      "method": "click",
      "targetDescription": "Load more button",
      "completionEvidence": "The results list increased from 20 to 40 items",
      "durationMs": 842
    }
  ]
}
```

| Status    | Meaning                                                |
| --------- | ------------------------------------------------------ |
| `applied` | The requested page state was visibly verified.         |
| `failed`  | The requested state was not verified; inspect `error`. |
| `skipped` | The action was not attempted.                          |

If a critical action is not `applied`, do not trust the captured content to represent the intended state.

## Keep interactions robust

* Describe visible text and intent, such as `click the "Pricing" tab`, instead of coordinates.
* Prefer one `perform` action followed by a short `wait` over several vague instructions.
* Use `scroll` for repeated movement; do not expand a simple scroll into multiple `perform` actions.
* Keep a fallback for login walls, CAPTCHA challenges, and interactions that depend on a user account.

## Supported endpoints

Actions are available on:

<CardGroup cols={2}>
  <Card title="Scrape Markdown" icon="file-lines" href="/api-reference/web-scraping/markdown">
    Apply actions before reading a page as Markdown.
  </Card>

  <Card title="Scrape HTML" icon="code" href="/api-reference/web-scraping/html">
    Apply actions before capturing rendered HTML.
  </Card>

  <Card title="Scrape images" icon="images" href="/api-reference/web-scraping/images">
    Apply actions before collecting page images.
  </Card>

  <Card title="Extract structured data" icon="table-cells" href="/api-reference/web-extraction/extract">
    Apply actions before extracting fields with a JSON Schema.
  </Card>
</CardGroup>

On Extract, actions run on the starting page before link discovery. Additional crawled pages do not repeat them. See the [extraction guide](/guides/extract-structured-data-from-websites) for its pricing and crawl controls.

## Next steps

<CardGroup cols={2}>
  <Card title="Scrape a webpage" icon="globe" href="/guides/scrape-websites-to-markdown">
    Combine actions with scraping and content-selection options.
  </Card>

  <Card title="Extract page images" icon="images" href="/guides/extract-page-images">
    Combine actions with image enrichment and deduplication.
  </Card>
</CardGroup>
