Skip to main content

Recipe

Long-running APIs are less reliable than smaller, retryable automations. When possible, break your automation into smaller units that can complete within the default timeout.
This recipe shows how to extend timeout limits for long-running automations using extendTimeout (TypeScript) or extend_timeout (Python). This example demonstrates processing a large list with delays between requests—common when scraping rate-limited websites page by page.
TypeScript
import { extendTimeout } from "@intuned/runtime";
import { BrowserContext, Page } from "playwright-core";

interface Book {
  title: string;
  price: string;
  url: string;
}

export default async function handler(
  params: { maxPages?: number },
  page: Page,
  context: BrowserContext
) {
  const allBooks: Book[] = [];
  const maxPages = params.maxPages || 50;
  let currentPage = 1;

  await page.goto("https://books.toscrape.com/");

  while (currentPage <= maxPages) {
    console.log(`Processing page ${currentPage} of ${maxPages}...`);

    // Extract books from current page
    const bookElements = page.locator("article.product_pod");
    const count = await bookElements.count();

    for (let i = 0; i < count; i++) {
      const book = bookElements.nth(i);
      const title = await book.locator("h3 a").getAttribute("title");
      const price = await book.locator(".price_color").textContent();
      const bookUrl = await book.locator("h3 a").getAttribute("href");

      if (title && price && bookUrl) {
        allBooks.push({
          title,
          price: price.trim(),
          url: `https://books.toscrape.com/${bookUrl}`,
        });
      }
    }

    console.log(`Completed page ${currentPage}. Books collected: ${allBooks.length}`);

    // Extend timeout after completing this unit of work
    extendTimeout();

    // Check if there's a next page
    const nextButton = page.locator(".next a");
    const hasNext = (await nextButton.count()) > 0;

    if (!hasNext || currentPage >= maxPages) {
      break;
    }

    // Add delay between page requests to respect rate limits
    await new Promise(resolve => setTimeout(resolve, 10000));

    // Navigate to next page
    await nextButton.click();
    await page.waitForLoadState("networkidle");

    currentPage++;
  }

  console.log(`Total books collected: ${allBooks.length}`);
  return allBooks;
}

Understanding timeouts

  1. Default timeout prevents stuck Runs — Each Run attempt times out after 600 seconds (10 minutes) by default, configurable via requestTimeout. This prevents stuck automations from wasting resources.
  2. extendTimeout() resets the timer — Each call resets the countdown to the original requestTimeout value. Call it as many times as needed, up to a 6-hour maximum total duration per Run.
  3. Correct placement is critical — Call extendTimeout() after completing each unit of work (a page, batch, or logical section). Each unit must finish within the original timeout. For automations requiring more than 6 hours, contact Intuned support.