Skip to main content
guide

run_script Helpers: Advanced Browser Automation in Deno

RTILA Team 9 min read

Written by the RTILA Team — the engineers and product builders behind RTILA X, building web automation software since April 2020.

Why Most Browser Scripts Fail (and How run_script helpers automation Fixes It)

You write a script that works perfectly on your machine. You run it a day later on a slightly slower connection, and everything breaks. Elements aren’t ready, consent banners block your clicks, and network requests that finished instantly before now hang your entire workflow.

This is the reality of browser automation without proper helpers. Raw Playwright or Puppeteer code gives you building blocks, but you’re on your own for timing, error recovery, and the thousand little edge cases that real websites throw at you.

RTILA X’s run_script command changes this with a curated set of run_script helpers automation utilities that handle the messy parts so you don’t have to. These helpers live inside the Deno runtime—a modern, secure JavaScript/TypeScript engine that powers every script you write. When we rebuilt our execution engine in version 8.3.0, we packed it with battle-tested functions that solve the problems we’ve encountered across thousands of real-world automation projects since our first GitHub release on April 10, 2020.

Let’s walk through exactly how these helpers work, why they matter, and how you can use them to build automation that survives the chaos of the open web.


The Script Structure That Powers Everything

Every run_script block in RTILA X follows the same signature:

export default async function(page, context, state, helpers) {
  // Your automation logic here
}

Four arguments, each with a distinct job:

  • page – Your Playwright-compatible page object. Click, type, evaluate—everything you’d expect from a browser tab.
  • context – Read-only information about the current run: project settings, the current URL, iteration index, and any configuration you’ve set in the RTILA X UI.
  • state – Your persistent memory layer. state.variables holds data that flows between commands in your workflow. state.memory survives across iterations, making it perfect for pagination tokens, session IDs, or counters that need to persist.
  • helpers – The star of this article. A collection of pre-built functions that handle the most common—and most error-prone—browser automation tasks.

This structure means you’re never starting from scratch. The runtime handles browser lifecycle, proxy routing, and stealth configuration. You focus on what makes your automation unique.


The Complete run_script helpers automation API

In our experience building RTILA X since 2020, we’ve identified the operations that cause the most headaches. Each helper below exists because we—or our users—hit a wall without it. Here’s the full toolkit at your disposal.

safeGoto(url, options) – Navigate to a URL and don’t return until the page is genuinely ready. Unlike a raw page.goto(), this helper waits for the network to settle, retries on failure (up to 3 times per URL when paired with RTILA X’s proxy health-check system), and handles redirects transparently. When we tested this on a paginated e-commerce site with inconsistent load times, safeGoto eliminated the random timeout errors that plagued our raw Playwright attempts.

waitForElement(selector, timeout) – Pause execution until an element appears in the DOM. Set a custom timeout or rely on the sensible default. This is the foundation of reliable automation—never assume an element exists just because the page loaded.

clickAndWait(selector, waitForSelector) – Click an element and then wait for a specific consequence. Click a “Load More” button? Wait for the new items to appear. Submit a form? Wait for the confirmation message. This helper chains two operations that almost always belong together.

waitForApiResponse(urlPattern, timeout) – Modern web apps are API-driven. You click a button, a fetch request fires, and the UI updates. This helper lets you wait for that background request to complete before proceeding—far more reliable than guessing when the DOM might update.

waitForNetworkIdle(timeout) – Wait until there are no in-flight network requests for a specified duration. Perfect for single-page applications where content loads in waves.

Data Extraction and Interaction

extractData(selector, type) – Pull text, HTML, attributes, or properties from elements matching a selector. This helper integrates directly with RTILA X’s Dataset Builder, so extracted data flows naturally into structured tables without manual formatting.

saveData(data) – Persist extracted information to your project’s dataset. Call this whenever you’ve gathered what you need from the current page, and RTILA X handles the rest—including deduplication when you configure it.

fillInput(selector, value) – Type text into a field the way a human would: character by character, with configurable delays. When humanoid mode is enabled (adjustable sensitivity from 0.5× to 1.5×), keystrokes include natural micro-variance.

autoScroll(direction, distance) – Scroll the page smoothly. Use it to trigger lazy-loaded content, infinite-scroll feeds, or simply to bring elements into view before interacting with them.

getElementsCount(selector) – A quick count of how many elements match a selector. Useful for conditional logic: “If there are more than 10 results, paginate; otherwise, stop.”

extractLinks(selector) – Collect all href attributes from matching elements. Feed the results directly into a for_each loop to crawl multi-page listings.

Network and API Interception

interceptApiData(urlPattern, callback) – Capture API responses as they happen. This is one of the most powerful helpers in the toolkit. Instead of scraping rendered HTML, you intercept the underlying JSON that the page itself receives. Combine this with RTILA X’s Network API Interception feature for a complete data-capture pipeline that’s faster and more reliable than DOM parsing.

handleConsent(selector) – Cookie banners and GDPR consent popups are the bane of web automation. This helper detects common consent patterns and dismisses them. You can target a specific selector or let the helper’s built-in detection take over.

handleDialog(accept) – Deal with browser-level dialogs (alert, confirm, prompt) programmatically. Accept or dismiss them without human intervention.

Utility Functions

resolvePath(relativePath) – Convert relative file paths to absolute paths based on your project directory. Essential when your script needs to read input files or write output.

sleep(milliseconds) – A simple, intentional pause. Sometimes you genuinely need to wait for an animation to complete or a third-party script to initialize. This helper makes those deliberate waits explicit in your code.

isVisible(selector) and isElementPresent(selector) – Boolean checks that let you branch your logic based on what’s actually on the page.

pressKey(key) – Simulate individual key presses, including special keys like Enter, Escape, or Tab.

revealHiddenElements(selector) – Some sites hide content behind hover states or collapsed sections. This helper temporarily modifies CSS to reveal hidden elements so you can interact with or extract from them.


state.memory and state.variables: Data That Survives

Automation isn’t just about the current page—it’s about carrying information forward. RTILA X gives you two persistence mechanisms, and knowing when to use each one is the difference between a fragile script and a production-ready workflow.

state.variables: Cross-Command Data Flow

Variables set with the set_variable command in your workflow are accessible inside any run_script block via state.variables. Use this when you need to pass data between different commands in the same project:

export default async function(page, context, state, helpers) {
  // Read a variable set by a previous command
  const searchTerm = state.variables.search_query;
  
  await helpers.fillInput('#search-box', searchTerm);
  await helpers.pressKey('Enter');
  await helpers.waitForElement('.results');
  
  // Store results count for the next command
  const count = await helpers.getElementsCount('.result-item');
  state.variables.result_count = count;
}

state.memory: Cross-Iteration Persistence

When your workflow loops—processing pages in a list, iterating through search results, or crawling paginated content—state.memory persists across every iteration. This is where you store pagination tokens, cumulative counters, or session identifiers:

export default async function(page, context, state, helpers) {
  // Initialize or retrieve the page counter
  if (!state.memory.currentPage) {
    state.memory.currentPage = 1;
  }
  
  await helpers.safeGoto(`https://example.com/listings?page=${state.memory.currentPage}`);
  
  // Extract items and save them
  const items = await helpers.extractData('.listing-card', 'text');
  await helpers.saveData(items);
  
  // Check if there's a next page
  const nextButton = await helpers.isVisible('.pagination .next:not(.disabled)');
  if (nextButton) {
    state.memory.currentPage += 1;
    // The workflow will loop to the next iteration
  } else {
    state.memory.hasMorePages = false;
  }
}

This persistence is what makes RTILA X’s Checkpoint & Resume feature so powerful. If your automation stops mid-crawl—whether due to a network hiccup, a CAPTCHA challenge, or a scheduled pause—the system remembers exactly where you were. Resume with --resume or --retry-failed, and state.memory picks up right where it left off.


Practical Examples: run_script helpers automation in Action

Theory is useful, but seeing these helpers work together on real tasks makes their value obvious. Here are three patterns we use constantly.

Example 1: Resilient Search and Extract

This script handles a search form, waits for results, and extracts structured data—all while dealing with the possibility of a consent banner:

export default async function(page, context, state, helpers) {
  // Navigate safely, handling any redirects
  await helpers.safeGoto('https://example-directory.com');
  
  // Dismiss consent banners if they appear
  await helpers.handleConsent('#cookie-accept');
  
  // Fill the search form
  await helpers.fillInput('#search', state.variables.keyword);
  await helpers.clickAndWait('#search-btn', '.results-container');
  
  // Wait for the API that actually powers the results
  await helpers.waitForApiResponse('/api/search');
  
  // Scroll to load all lazy-loaded results
  await helpers.autoScroll('down', 2000);
  
  // Extract and save
  const names = await helpers.extractData('.business-name', 'text');
  const phones = await helpers.extractData('.phone-number', 'text');
  
  await helpers.saveData({ names, phones });
}

Example 2: API Interception for Clean Data

When a site loads data via XHR or fetch, scraping the rendered HTML is inefficient. Intercept the API response directly:

export default async function(page, context, state, helpers) {
  let capturedData = null;
  
  // Set up interception before navigating
  helpers.interceptApiData('/api/products', (data) => {
    capturedData = data;
  });
  
  await helpers.safeGoto('https://example-store.com/products');
  await helpers.waitForNetworkIdle(5000);
  
  if (capturedData && capturedData.products) {
    await helpers.saveData(capturedData.products);
  }
}

Example 3: Multi-Page Crawl with Memory

Crawl a paginated listing, stopping when there are no more pages:

export default async function(page, context, state, helpers) {
  const baseUrl = 'https://example-blog.com/articles';
  
  if (!state.memory.page) {
    state.memory.page = 1;
    state.memory.allArticles = [];
  }
  
  await helpers.safeGoto(`${baseUrl}?page=${state.memory.page}`);
  await helpers.waitForElement('article');
  
  const articles = await helpers.extractLinks('article h2 a');
  state.memory.allArticles.push(...articles);
  
  const hasNext = await helpers.isVisible('.pagination .next');
  
  if (hasNext) {
    state.memory.page += 1;
    // Continue to next iteration
  } else {
    await helpers.saveData(state.memory.allArticles);
    state.memory.finished = true;
  }
}

Build Automation That Actually Survives

The difference between a script that works once and a workflow you can trust for months comes down to the helpers you use. Raw browser APIs give you control. run_script helpers automation gives you resilience—the kind that handles slow networks, unexpected popups, and the general unpredictability of the web.

In our experience, the combination of safeGoto for navigation, waitForApiResponse for timing, and state.memory for persistence eliminates roughly 80% of the failures we see in scripts that don’t use these helpers. The remaining 20% comes from site-specific quirks that you’ll handle with custom logic—but at least you’ll be building on a solid foundation.

Ready to put these helpers to work? Download RTILA X and start building automation that survives the real world. The Free Community plan gives you one device and unlimited runs—no credit card required. If you need more devices, paid plans start at $9/month or $149 lifetime, all backed by a 60-day money-back guarantee.


Frequently Asked Questions

What’s the difference between state.variables and state.memory in run_script helpers automation?

state.variables carries data between different commands within a single workflow execution. Use it when a set_variable command needs to pass information to a subsequent run_script block. state.memory persists across iterations—when your workflow loops through multiple URLs or repeats a sequence, state.memory retains its values from one iteration to the next. Think of variables as intra-run communication and memory as cross-iteration storage.

Can I use run_script helpers alongside regular RTILA X commands?

Absolutely. A typical workflow mixes visual commands (like goto, click, extract_data) with run_script blocks that contain custom logic. The helpers inside run_script complement the command-based approach—use visual commands for straightforward steps and drop into run_script when you need conditional logic, API interception, or complex data manipulation that’s easier to express in code.

Do the Deno browser automation helpers work with RTILA X’s stealth features?

Yes. The entire helper library runs inside RTILA X’s stealth browser engine, which includes the Humanoid Mouse with cubic Bézier curve movement, configurable sensitivity levels, and automatic fingerprint management. When you call helpers.fillInput(), the typing respects your humanoid settings. When you use helpers.safeGoto(), the request routes through your configured proxy with automatic timezone and locale matching. The helpers don’t bypass stealth—they work within it.


RTILA X automates actions you could perform manually. Always review each platform’s Terms of Service and applicable data-privacy laws before automating.

Written by the RTILA X team. We build and test every feature we write about on real websites, every week. Our browser automation engine has evolved through every release since our first public launch on April 10, 2020, through our AppSumo debut in 2021 (116 reviews, 4.7/5 rating), our Product Hunt launch in 2023 (5/5 rating), and our exhibition at GITEX Africa 2026 in Marrakech. When we document a helper function, it’s because we’ve used it to solve a real problem.

Sources and Verification: AppSumo reviews at https://appsumo.com/products/marketplace-rtila-growth-hacking-marketing-automation-software/reviews/, Trustpilot at https://www.trustpilot.com/review/rtila.com, Product Hunt at https://www.producthunt.com/products/rtila-studio, GitHub releases at https://github.com/rtila-corporation/rtila-releases/releases.

run_script deno automation browser helpers web scraping RTILA X

Written by the RTILA X team, the engineers and product builders who develop RTILA X. This article reflects first-hand experience building and maintaining web automation software since April 2020.

Learn about our team