How to Take Website Screenshots Automatically in Retool
Build a Retool workflow that triggers Playwright or a screenshot API, captures pages reliably, and stores or routes the resulting image.

Use a Retool Workflow as the trigger and coordinator, then call a separately hosted browser worker or screenshot API. Retool Workflows can start on a schedule or webhook. The workflow validates the request, sends it through a REST API resource, and then stores the returned image URL or key, updates a record, or sends a notification. The browser itself should run in a capture service such as a Playwright worker unless you have independently verified that your Retool execution environment supports browsers.
This design follows the capabilities documented by Retool Workflows, Retool REST API resources, and Microsoft’s Playwright screenshot documentation. Retool’s documentation does not describe a native website-screenshot block or promise that a normal workflow JavaScript block can launch Chromium.
Architecture: Retool trigger to captured image
A reliable implementation has five boundaries:

- Trigger: a schedule for recurring captures, or a webhook when another system requests one.
- Validation: check the URL, viewport, capture mode, and allowed destination before making a network request.
- Capture: call a hosted service or your own Playwright worker.
- Output: receive image bytes, a generated file, an object-storage key, or a service URL.
- Follow-up: save metadata, compare with a prior capture, update a Retool table, or notify a team.
The external browser step is an architectural inference from the documented Retool HTTP and workflow features plus Playwright’s browser API. Confirm the runtime, storage, concurrency, and retry behavior of whichever capture service you select.
1. Create and publish the Retool Workflow
Choose a trigger
- Schedule trigger: use for hourly, daily, or weekly visual monitoring.
- Webhook trigger: use when a deployment, CMS event, form submission, or another application requests a capture.
Retool runs the published, versioned release for automatic schedule and webhook executions. Saving edits is not enough: publish after changing the workflow or its request contract.
Define an explicit payload
Keep the request small and predictable. A useful payload is:
{
"url": "https://example.com/pricing",
"viewport": { "width": 1440, "height": 900 },
"fullPage": true,
"selector": null,
"waitFor": null,
"delayMs": 0,
"output": "png"
}
These fields are implementation choices, not a universal Retool contract. Your worker can add options for dark mode, a device profile, authentication, or a storage destination. Validate URLs against an allowlist when possible. Do not let untrusted users turn a general-purpose browser worker into a way to fetch internal network addresses.
Add validation and branching
Use a JavaScript block or equivalent workflow step to reject malformed input before the HTTP call. Check that:
- The URL uses HTTPS unless HTTP is an intentional requirement.
- The hostname is allowed for the workflow’s purpose.
- Viewport width and height are within your service’s limits.
fullPageandselectorare not contradictory for your worker.- Timeouts and delays have finite maximums.
Branch after the capture call on HTTP status and on the worker’s application-level result. Return a useful error object containing the target URL, a request ID, and a short cause, while keeping credentials out of logs.
2. Configure a Retool REST API resource
Retool’s REST integration can call standard HTTP endpoints using API-key, bearer-token, basic, OAuth 2.0, or custom-header authentication. Configure the credential in the resource rather than placing it in workflow JavaScript. Retool states that resource credentials are stored encrypted.
- Create a REST API resource for the browser worker or hosted screenshot API.
- Set the base URL and authentication method supported by the endpoint.
- In the workflow, add a POST request with the validated JSON payload.
- Set a request timeout appropriate for slow pages and full-page scrolling.
- Map the response fields to later blocks: image URL, storage key, verdict, dimensions, and request ID.
If the capture service returns binary data directly, configure the resource and downstream storage for binary handling. Returning a short-lived object-storage URL is often simpler for later workflow steps, but the storage design is your responsibility.
3. Run the browser capture with Playwright
Host this code as a small HTTP service, job worker, or container. The worker receives the validated request, opens a browser page, navigates to the target, and calls page.screenshot(). Playwright supports viewport screenshots, full-page screenshots, and screenshots of a selected element. It can write to a path or return image bytes.
import express from 'express';
import { chromium } from 'playwright';
const app = express();
app.use(express.json({ limit: '32kb' }));
const browserPromise = chromium.launch({ headless: true });
app.post('/capture', async (req, res) => {
const {
url,
viewport = { width: 1440, height: 900 },
fullPage = false,
selector,
waitFor,
delayMs = 0
} = req.body ?? {};
if (typeof url !== 'string' || !url.startsWith('https://')) {
return res.status(400).json({ error: 'url must be an HTTPS URL' });
}
if (!Number.isInteger(viewport.width) || !Number.isInteger(viewport.height)) {
return res.status(400).json({ error: 'viewport dimensions must be integers' });
}
const browser = await browserPromise;
const context = await browser.newContext({ viewport });
const page = await context.newPage();
try {
await page.goto(url, { waitUntil: 'networkidle', timeout: 45000 });
if (waitFor) await page.locator(waitFor).waitFor({ state: 'visible', timeout: 15000 });
if (delayMs > 0) await page.waitForTimeout(Math.min(delayMs, 10000));
let image;
if (selector) {
image = await page.locator(selector).screenshot({ type: 'png' });
} else {
image = await page.screenshot({ type: 'png', fullPage });
}
// Upload image to your object storage here and return its key or URL.
res.json({ ok: true, contentType: 'image/png', bytes: image.length });
} catch (error) {
res.status(502).json({ error: 'capture_failed', message: String(error) });
} finally {
await context.close();
}
});
app.listen(process.env.PORT || 3000);
Install Playwright and its browser binaries in the worker image. Keep the browser process warm for repeated jobs, but create a fresh context per request so cookies and storage do not leak between captures. If the page contains content below the fold, set fullPage: true. For one component, pass a stable CSS selector and use a locator screenshot.
4. Store, compare, and route the result
After the REST step, add workflow blocks for your business action:
- Write the returned URL or storage key and capture metadata to a Retool database resource.
- Attach the image to a record or ticket.
- Send a notification only when the capture succeeds or differs from a baseline.
- Record the target URL, timestamp, viewport, full-page flag, worker version, and error cause.
For visual regression, normalize the capture settings first. A different viewport, font load, animation state, timezone, or authenticated session can create a difference unrelated to a code change. Disable animations in the worker with injected CSS when your use case permits, and wait for a known selector rather than relying only on a fixed delay.
Capture choices and their trade-offs
| Requirement | Recommended choice | Things to verify |
|---|---|---|
| Recurring monitoring | Retool schedule trigger | Published workflow version and schedule timezone |
| On-demand capture | Retool webhook trigger | Authentication, replay protection, and input validation |
| Visible viewport only | page.screenshot() |
Viewport width and height |
| Entire scrollable page | fullPage: true |
Lazy-loaded content and very tall pages |
| One component | locator(selector).screenshot() |
Stable selector and element visibility |
| Private page | Authenticated browser context | Secret storage, session expiry, and isolation |
| High volume | Queue-backed worker or hosted API | Concurrency, retries, rate limits, and cost |
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the ScreenshotNeo API documentation for the complete parameter list. The same request can be called from a Retool REST resource.
Retool or cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo includes full-page and CSS-element capture, dark mode, device presets, arbitrary viewports, retina scale, PDF paper sizes and page ranges, custom CSS and JavaScript, clicks, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
There are 1,000 free shots each month with no card. Paid plans start at $5 for 3,000 shots; higher plans are Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000). Yearly billing gives two months free, and every feature is on every plan. Create a free ScreenshotNeo account.
Troubleshooting Retool screenshot workflows
The workflow runs but no image is produced
Cause: the worker returned metadata without uploading bytes, or a later storage block received the wrong response field. Fix: log the response schema, map the exact URL or key, and verify that the storage write runs only after a successful capture.
Navigation times out
Cause: slow third-party resources, a page that never reaches network idle, or a blocked domain. Fix: use a bounded navigation timeout, wait for a known page selector, block nonessential requests where appropriate, and record the final URL and error.
The screenshot stops before lazy content
Cause: the page loads images only after scrolling. Fix: use full-page capture and add a controlled scroll or selector wait in the worker. Confirm that the page’s lazy-loading behavior has settled before capture.
Element capture fails with “not visible”
Cause: the selector matches nothing, the element is hidden, or the page has not finished rendering. Fix: use a stable selector, wait for visibility, and return the matched element count in diagnostic logs.
Webhook requests create duplicate captures
Cause: retries or client replay. Fix: require an idempotency key, persist it before capture, and return the existing result for a repeated key.
Credentials appear in logs
Cause: putting secrets in payloads or debug output. Fix: store authentication in the Retool resource, redact authorization headers, and restrict who can view workflow logs.
Private pages show a login screen
Cause: the worker has no valid session or the session expired. Fix: provide an isolated authenticated context, refresh credentials safely, and never share a browser context between tenants or unrelated jobs.
Performance, reliability, and cost planning
- Reuse browsers, isolate contexts: a warm Playwright browser reduces startup work; a new context per request protects cookies and local storage.
- Bound every wait: navigation, selector waits, delays, and queue visibility timeouts need explicit limits.
- Retry selectively: retry transient network failures with backoff. Do not blindly retry invalid URLs, authentication failures, or deterministic selector errors.
- Control concurrency: cap parallel pages so CPU, memory, and bandwidth remain predictable. Confirm the selected service’s limits.
- Cache intentionally: caching can reduce repeated work, but visual monitoring may require a short or zero TTL.
- Measure the whole path: record trigger time, queue delay, navigation time, rendering time, upload time, HTTP status, and final outcome.
- Estimate storage: full-page PNGs can be large. Consider WebP or JPEG when exact lossless pixels are unnecessary, and apply lifecycle rules to old captures.
- Protect destinations: allowlist domains and block internal address ranges when accepting user-supplied URLs.
With a self-hosted worker, your main costs are browser compute, bandwidth, storage, and engineering operations. A hosted API trades runtime maintenance for a per-capture plan; verify its concurrency, timeout, retention, and failure-billing rules before committing.
FAQ
Can a Retool JavaScript block take a screenshot by itself?
The cited Retool material documents JavaScript and HTTP workflow steps, but not browser support inside a normal workflow block. Use a separately hosted Playwright worker or screenshot API unless your environment has been verified to run a browser.
Should I use a schedule or webhook?
Use a schedule for recurring snapshots and a webhook for event-driven captures such as deployments or content changes. You can use both in separate workflows that share the same capture endpoint.
How do I capture only a card or chart?
Send a stable CSS selector to the worker and call Playwright’s locator screenshot method. Wait for the element to become visible before capturing.
How do I make a full-page capture?
Pass fullPage: true to Playwright’s screenshot call, or use the equivalent full-page option in your hosted API. Wait for lazy-loaded content first.
Where should the image live?
Return image bytes for immediate processing, or upload them to object storage and pass a key or signed URL through the remaining Retool steps. Choose retention and access controls that match the sensitivity of the page.
Can AI agents request captures?
Yes. ScreenshotNeo includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for MCP clients such as Claude and Cursor.


