Categories:Scraping Use Cases,Scraping ToolsView as Markdown
How to Build a Bulk Web Scraper in Retool with Scrape.do (Low Code)

Growth
Step-by-step guide to integrate Scrape.do with Retool for automated web scraping workflows
Scrape.do integrates with Retool through a REST API resource. Build a workflow that reads a list of URLs, scrapes each one through Scrape.do, parses the response with AI or JavaScript, and writes the results back to Retool Database or any other resource in your organization.
1. Create the Scrape.do Resource
Retool keeps connection details in resources, separate from the workflows that use them. Setting Scrape.do up as a resource means your token is stored once and never appears inside a workflow or its exported JSON.
- From your Retool dashboard, go to Resources and click Create new > Resource.
- Search for and select REST API.
- Configure the resource:
- Name - Enter
Scrape.do - Base URL - Enter
https://api.scrape.do/ - Authentication - Select None
- Name - Enter
- Under URL parameters, click Add and enter:
- Key -
token - Value -
{{ configVars.SCRAPE_DO_TOKEN }}
- Key -
- Click Save changes.
Now add the token itself. Go to Settings > Configuration variables, click Add, name it SCRAPE_DO_TOKEN, and paste your API token from the dashboard.
Storing the token as a configuration variable rather than typing it into the resource keeps it out of exports and lets you use different tokens in staging and production.
2. Prepare Your Data Source
The workflow reads URLs from a table and writes results back to the same table. Create it in Retool Database:
create table scrape_targets (
id serial primary key,
url text not null,
status text not null default 'pending',
content text,
scraped_at timestamptz
);
Insert a few URLs to test with:
insert into scrape_targets (url) values
('https://us.amazon.com/dp/B0BLRJ4R8F'),
('https://us.amazon.com/dp/B0CHX1W1XY');
If your URLs already live in Postgres, MySQL, Snowflake, or Google Sheets, use that resource instead. Only the query in the next step changes.
3. Create a Workflow and Add Trigger
- From your Retool dashboard, go to Workflows and click Create new > Workflow.
- Click the start block to choose a trigger:
- Manual - For testing and on-demand runs
- Schedule - For automated hourly or daily scraping
- Webhook - To trigger scraping from external sources
- Click the + button and add a Resource query block. Name it
getUrls. - Configure the block:
- Resource - Select your database
- Query - Enter the statement below
select id, url from scrape_targets
where status = 'pending'
limit 500
- Click Run block to confirm it returns your URLs.
4. Loop Through URLs with Scrape.do
A single Resource query scrapes one URL. To scrape a list, wrap it in a Loop block.
- Click the + button after
getUrlsand add a Loop block. Name itscrapeLoop. - Configure the loop:
- Iterable - Enter
{{ getUrls.data }} - Execution mode - Select Batch
- Batch size - Set to your Scrape.do plan concurrency
- Iteration delay - Set to
250ms
- Iterable - Enter
- Inside the loop, select Resource and choose
Scrape.do. - Configure the inner query:
- Method - Select GET
- URL - Leave the path empty, the base URL already points at the API
- Under URL parameters, add these one by one:
- url -
{{ encodeURIComponent(value.url) }} - output -
markdownfor AI extraction, orrawto parse HTML yourself - render -
falseby default, set totruefor JavaScript heavy sites - super -
trueto use residential and mobile proxies on protected targets - geoCode - Country code for proxy location, for example
us,uk,de
- url -
encodeURIComponent()is required. Retool does not encode URL parameters for you, so a target URL containing?or&will break the request and return an error from the API. This is the single most common cause of failed requests in Retool.
- Click Run block and confirm the loop returns scraped content for each row.
5. Extract Data from Response
You now have raw HTML or markdown for every URL. There are two ways to turn that into structured data.
Option A: Use AI to Extract Data
Best for scraping different websites with varying structures. AI returns a consistent format regardless of the source layout.
- Click the + button after
scrapeLoopand add an AI Action block, or a Resource query pointed at your Anthropic or OpenAI resource. - Configure the block:
- Action - Select Generate text
- Model - Select a model such as
claude-sonnet-4-5-20250929 - Prompt - Use the structure below
{{ scrapeLoop.data[0] }}
Analyze the markdown data in this document and extract ASIN, Product Name,
Product Price, Review Rating, and Review Count as a structured JSON object.
Return only the JSON, with no explanation and no code fences.
- Add a JavaScript block after it to parse the response into rows:
return aiExtract.data.map((raw, i) => {
const target = getUrls.data[i];
let parsed = {};
try {
parsed = JSON.parse(raw);
} catch (e) {
parsed = {};
}
return {
id: target.id,
url: target.url,
content: JSON.stringify(parsed),
status: Object.keys(parsed).length ? "ok" : "failed",
scraped_at: new Date().toISOString()
};
});
Option B: Use JavaScript for Extraction
If you are scraping a single site with a consistent layout, JavaScript is faster and consumes no AI credits. It also handles responses too large for a model context window.
- Click the + button after
scrapeLoopand add a JavaScript block. Name itnormalize. - Paste the extraction logic:
return scrapeLoop.data.map((html, i) => {
const target = getUrls.data[i];
const ok = html !== null && html !== undefined && html !== "";
const pick = (re) => {
const m = ok ? html.match(re) : null;
return m ? m[1].replace(/\s+/g, " ").trim() : null;
};
return {
id: target.id,
url: target.url,
name: pick(/<span id="productTitle"[^>]*>([\s\S]*?)<\/span>/),
asin: pick(/"asin":"([A-Z0-9]{10})"/),
price: pick(/<span class="a-offscreen">\$([0-9.,]+)<\/span>/),
rating: pick(/(\d+\.?\d*)\s*out of/),
status: ok ? "ok" : "failed",
scraped_at: new Date().toISOString()
};
});
- Click Run block to verify the fields come back populated.
6. Save Results to Your Database
- Click the + button and add a Resource query block. Name it
save. - Configure the block:
- Resource - Select your database
- Action type - Select Bulk update via primary key
- Table - Select
scrape_targets - Primary key - Select
id - Array of records to update - Enter
{{ normalize.data }}
Add a Response block at the end to return a run summary:
{{ {
total: normalize.data.length,
ok: normalize.data.filter(r => r.status === 'ok').length,
failed: normalize.data.filter(r => r.status === 'failed').length
} }}
7. Test and Activate
Before enabling the workflow, run it end to end.
- Click Run at the top of the canvas to execute every block in sequence.
- Verify that:
- The Loop block returns content for each URL rather than nulls
- Extraction produces populated fields, not empty strings
- The rows in
scrape_targetsmove frompendingtook
Downloads:
scrapedo-retool-workflow.json- the importable Retool workflow
Import it into your own Retool organization to get the exact same workflow.
Importing a workflow does not create its resources. Complete step 1 first, using the exact resource name
Scrape.do, or the imported blocks will have nothing to point at.
- Once the run is clean, you can:
- Switch the trigger from Manual to Schedule and set a cron expression
- Click Enable in the top right to activate the workflow
- Use a Webhook trigger to scrape URLs sent from your Retool apps or external services
- Build a Retool app on top of
scrape_targetswith a table and a button that triggers this workflow on demand

Growth

