CrawlVolt
<- Field Notes
Guide / Browser actions

Extract content loaded by JavaScript.

Click a button, wait for a visible state in the DOM, then validate the extracted Markdown against a controlled demonstration page.

Reproduce the missing-content problem.

A page can load successfully while the content you need is still absent. Some sites render their text after a network response; others require a click. Start by identifying a specific DOM state that means your content is ready.

Open our JavaScript rendering demo and select Load details. After 350 milliseconds, a sentence appears inside #result and its data-ready attribute becomes true. Before that click, the result element is empty.

The downloadable HTML fixture makes this behavior easy to inspect. A plain HTML download includes the script source, but it does not execute the click handler or populate the result element.

Create a CrawlVolt API key. Give it a name for this project and copy the secret when it appears. It is shown once.

Already registered? Sign in and continue to API keys. Keep the secret in your local environment or credential store.

Run the same interaction through Browse.

Use Node.js 24. Save rendered_page.mjs and the shared page_to_markdown.mjs helper in one folder, then run:

Terminal
export CRAWLVOLT_API_KEY="your_api_key"
node rendered_page.mjs https://www.crawlvolt.com/examples/rendered-demo.html

The expected Markdown includes Ready for collection: 42 items. This fixed sentence is an assertion for our demo page. The script also checks the ordered action results so a successful HTTP response cannot hide a failed click or wait.

Wait for a selector, then inspect the content.

Node.js · rendered_page.mjs
import { pathToFileURL } from 'node:url';
import { request, requireMarkdown } from './page_to_markdown.mjs';

export const renderActions = [
  { action: 'click', selector: '#load-details' },
  { action: 'wait', selector: '#result[data-ready=true]', timeout_ms: 10_000 },
];

export function requireRenderedPage(result) {
  if (!Array.isArray(result?.action_results) || result.action_results.length !== renderActions.length ||
      result.action_results.some((action, index) => action.ok !== true || action.action !== renderActions[index].action)) {
    throw new Error('A browser action failed or its result is missing. Inspect action_results.');
  }
  const markdown = requireMarkdown(result);
  if (!markdown.includes('Ready for collection: 42 items.')) {
    throw new Error('The expected demo content is missing from the rendered output.');
  }
  return markdown;
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  try {
    const url = process.argv[2] || 'https://www.crawlvolt.com/examples/rendered-demo.html';
    const result = await request('browse', {
      url, actions: renderActions, formats: ['markdown'], timeout_secs: 30,
    });
    console.log(requireRenderedPage(result));
  } catch (error) {
    console.error(error.message);
    process.exitCode = 1;
  }
}

The actions run in order: click #load-details, then wait up to ten seconds for #result[data-ready=true]. Browse extracts Markdown after the actions. The request also has a 30-second server timeout and the shared helper has a 60-second client timeout.

Use an observable readiness condition when possible. A fixed sleep always waits the full duration and can still be too short on a slow page. A selector wait can continue as soon as the state exists, but you must still check that the extracted content is useful.

Separate action failures from content failures.

  • Click fails: check the selector, overlays, consent dialogs and whether the element is available on the chosen viewport.
  • Wait times out: confirm that the interaction creates the exact attribute or element you selected. A permanent loading state can indicate a failed page request.
  • Actions succeed but text is missing: inspect the rendered page and extraction output. An element's existence does not guarantee its text was included.
  • HTTP error or client timeout: review Activity before retrying. The script does not automatically repeat browser interactions.

Only perform interactions you intend on pages you are allowed to automate. This example clicks a local-content demonstration button; it does not submit a form, sign in or make a purchase.

Use the smallest interaction your page needs.

If content appears automatically, try Scrape with an appropriate wait_for selector first. Use Browse when the page needs an action such as a click or fill. Replace both the action selectors and the expected-content assertion when adapting this demo to your own page.

Browse uses a different credit model from a single Scrape request. Review the action limits and billing reference and measure a small run before scheduling it. For typed fields after interaction, continue with structured JSON extraction.