Stealth Web Scraper & API - HTML, Text & Contacts · Apify
Stealth Web Scraper - HTML, Text & Contact Data avatar

Stealth Web Scraper - HTML, Text & Contact Data

Pricing

from $4.00 / 1,000 pages

Go to Apify Store
Stealth Web Scraper - HTML, Text & Contact Data

Stealth Web Scraper - HTML, Text & Contact Data

Extract text, HTML, CSS-selected fields and public contact details from supplied web pages across countries. Pay per successful page row plus a small start fee.

Pricing

from $4.00 / 1,000 pages

Rating

0.0

(0)

Developer

kane liu

kane liu

Maintained by Community

Actor stats

2

Bookmarked

324

Total users

17

Monthly active users

8 hours ago

Last modified

Features

Limited permissions

Share

Get text, rendered HTML, selected fields and public contact details from web pages as rows you can export.

  • One successful page per row, with its URL, title and collection time.
  • Plain text for analysis, HTML for further parsing, or both.
  • CSS selectors for page-specific fields such as headings and prices.
  • Public phones, emails, addresses and social links when found on the supplied page.
  • A separate dataset for failed pages, without a row charge.
  • Pay $5.00 per 1,000 delivered rows on the Free plan, plus a small start fee.

What does Stealth Web Scraper do?

Stealth Web Scraper collects the public HTTP or HTTPS pages you supply in urls. It is a general page scraper, not a search engine or a site-specific catalog. Use it for page content, business contact pages or scheduled comparisons of the same URLs.

  • Fetch pages: enter up to 40 URLs. Duplicate URLs are removed after validation, keeping the first occurrence.
  • Choose content: outputFormat returns text, html or both.
  • Select fields: extractSelectors adds named values to the same page row.
  • Read contact details: available contact attributes are included automatically, not as separately billed rows. There is no contact add-on switch.

The Actor has one URL-fetching mode and no fixed country catalog. proxyCountry is an optional country hint, not a guarantee that a regional page is accessible. It does not translate content or follow links to find more pages.

What data can you extract from Stealth Web?

Successful page rows

FieldDescriptionExample
rowTypeIdentifies a page row.page
idValidated requested URL used as the row identity.https://example.com
urlURL associated with the requested page.https://example.com
titlePage title, or heading when no title is found.Example Domain
statusCodeHTTP status of the page result.200
blockedWhether the page was blocked. Successful rows are not blocked.false
textReadable page text when requested.Example Domain...
htmlRendered HTML when requested.<h1>Example Domain</h1>
extractedValues named by your CSS selector map.{"heading":"Example Domain"}
waitSelectorSatisfiedWhether the required selector appeared, when supplied.true
dataQualityQuality of the delivered content.ok or partial
phonesPhone attributes, including type and normalized number when available.[{"value":"+1 555 0100","type":"voice"}]
emailsEmail attributes found on the page.[{"value":"name@example.com","type":"public"}]
address.streetStreet text, including a house number when shown.123 Example Street
address.unitExplicit suite or apartment marker, not the house number.Suite 100
address.localityCity or locality when identifiable.Example City
org.nameOrganization name when identifiable.Example Company
web.socialsSocial profile URLs found on the page.[]
geoLatitude and longitude when present in the source.{} when absent
personExplicit person details when available.{} when absent
leadAbsencesReasons contact attributes could not be extracted.{"phones":{"reason":"not_extracted","surface":"https://example.com"}}
htmlTruncatedHTML was shortened.true
textTruncatedText was shortened.true
extractedTruncatedSelector values were shortened.true
scrapedAtCollection timestamp.2026-10-24T08:00:00+00:00

Failed page rows

These go to the separate failures dataset and are not charged as successful rows.

FieldDescriptionExample
urlPage that failed or could not be delivered.https://example.com/missing
reasonFailure or delivery-limit reason.charge_limit_reached
statusCodeHTTP status when available.404
blockedWhether access was blocked, when known.true

How to scrape Stealth Web

  1. Open Stealth Web Scraper in Apify Console. A free Apify account is enough to try it.
  2. Enter one public page in urls. Leave the advanced options unchanged.
  3. Set maxResults to 1 for the example. Each URL already produces at most one row; use fewer URLs to make the run smaller.
  4. Set your maximum run cost, click Start, and wait for completion.
  5. Open the default dataset and download JSON, CSV or Excel, or read the rows through the API.

Empty input starts an example run using https://example.com, https://example.org and https://example.net, with at most three rows. Normal start and row prices apply; it is not a free preview. Supplying urls replaces the example targets. There is no mode to select.

Fetch pages and select fields

Set extractSelectors when you need specific elements. A selector matching one element returns a single value; multiple matches return a list. Values prefer the element's content, href, src or value attribute, then its text. Missing values may be omitted from the delivered row.

Use waitForSelector when a particular element must appear. If it does not appear, the page goes to the failures dataset instead of being billed as a successful row.

What to fill in

FieldWhat to enterDefault
urls1–40 full public HTTP or HTTPS URLs.Three example URLs when empty
maxResultsRows per URL; any value of 1 or more gives at most one page row per URL.70; form example 3
extractSelectorsObject mapping your field names to CSS selectors.{}
outputFormathtml, text or both.text
waitForSelectorCSS selector required before extraction.Empty
maxConcurrencyRequested number of pages processed together, from 1 to 4; may be reduced.3
pageTimeoutPage-loading allowance in seconds, from 30 to 120.45
proxyCountryOptional two-letter country hint, such as us or gb.Empty
proxyGroupOptional connection preference; normally leave unchanged.RESIDENTIAL
cookiesOptional cookie objects with name and value, plus applicable scope fields.Empty
headersOptional object of request header names and string values.Empty
gotoWaitUntilNavigation milestone: commit, domcontentloaded, load or networkidle.domcontentloaded
blockResourcesSkip images, fonts and media where supported.true
{
"urls": ["https://example.com"],
"maxResults": 1,
"outputFormat": "both",
"extractSelectors": {"heading": "h1"},
"waitForSelector": "h1"
}
  • url, startUrls and pageUrls are aliases for urls. URL objects such as {"url":"https://example.com"} are accepted too.
  • maxItems and limit are aliases for maxResults, not total-run limits.
  • Unknown fields are not rejected by the input resolver. Close misspellings can be corrected; other unknown fields are ignored and reported.
  • The INPUT_REPORT record in the run's key-value store lists input resolution, corrections and ignored fields.
  • Invalid URLs fail before collection, for example INVALID_INPUT: urls[0] — use a full public web address such as https://example.com/page. Other validation messages can use different wording. No page rows are charged for rejected input; the start fee can still apply.

Output example

Illustrative successful row with the selector example above. Contact objects are empty here because no contact details were extracted.

{
"rowType": "page",
"id": "https://example.com",
"url": "https://example.com",
"statusCode": 200,
"blocked": false,
"title": "Example Domain",
"text": "Example Domain...",
"html": "<html><body><h1>Example Domain</h1></body></html>",
"extracted": {"heading": "Example Domain"},
"waitSelectorSatisfied": true,
"dataQuality": "ok",
"billable": true,
"phones": [],
"emails": [],
"address": {},
"org": {},
"web": {"socials": []},
"leadReveal": "absent",
"scrapedAt": "2026-10-24T08:00:00+00:00"
}

Export the dataset as JSON, CSV, Excel, XML, RSS or an HTML table. JSON preserves nested contact objects. RUN_REPORT and RUN_SUMMARY contain run counts and failure summaries; ERROR_SUMMARY contains failure details when present.

Why use this Stealth Web scraper?

  • Pay for delivered rows. Failed pages have no row charge; the start fee is separate.
  • Keep delivered work. Rows already written remain available if a later page fails or the run stops. Unfinished pages are not guaranteed to be delivered.
  • Combine URLs. Supply several pages in one run without repeating duplicate inputs.
  • See missing data. Empty attributes and leadAbsences distinguish missing extraction from a verified absence on the site.
  • Reuse the output. Store page text for analysis or compare repeated runs by URL and timestamp.
  • No proxy setup. You do not need to set up proxies for normal use.

How much does it cost to scrape Stealth Web?

The Actor uses pay-per-event pricing: a $0.005 start event plus a charge for each successful page row written to the default dataset. Platform usage is included in these prices.

Apify planPrice per rowPrice per 1,000 rows
Free$0.005$5.00
Starter$0.005$5.00
Scale$0.0045$4.50
Business$0.004$4.00
Platinum$0.004$4.00
Diamond$0.004$4.00

The start event is counted per GB of memory, with a minimum of one event. At the default 512 MB, one start costs $0.005. For three successful URLs on the Free plan, the example is 3 × $0.005 + $0.005 = $0.020.

  • Each unique URL yields at most one row. maxResults defaults to 70, but does not create more rows or cap the number of supplied URLs.
  • This Actor accepts at most 40 URLs per run, on every plan. It does not split requests into 70-row batches.
  • Free-plan users are limited to 5 runs and 40 URLs per run by this Actor. The smaller 40-URL limit applies here, not a generic 200-row allowance.
  • Maximum cost per run (maxTotalChargeUsd in the API) limits delivery across the run. The Actor stops delivering additional billable rows when the available charge allowance is exhausted.

What happens when the run timeout is too short

If collection needs more time, the run can pause with status ABORTED and a message such as “Queued on the collection backend; this run will resume automatically when results are ready (about Ns).” For this automatic pause, wait rather than starting another run.

When results are ready, the same run resumes with “Resumed: delivering N rows.” It writes to the same dataset and can finish as SUCCEEDED. Rows already delivered are skipped, so they are not delivered or charged twice. This pause can happen at most three times per run. Keep the default 900-second timeout to reduce interruptions. A genuine collection failure can still fail the run.

The synchronous API endpoints run-sync and run-sync-get-dataset-items wait at most 300 seconds and may return an empty dataset while the run is paused. SDK calls that wait for completion can also return at the temporary stop. For longer jobs, start asynchronously and read the dataset after SUCCEEDED, or use an ACTOR.RUN.SUCCEEDED webhook.

Use with AI agents and the API

Apify MCP server (Claude, ChatGPT, Cursor, VS Code)

Add this Actor to an MCP client that supports remote servers:

https://mcp.apify.com?tools=lentic_clockss/stealth-web-scraper

Sign in with Apify through OAuth, or send Authorization: Bearer <YOUR_APIFY_TOKEN>. The tool starts a run and returns run and dataset IDs; the agent can read rows with get-dataset-items when available in its tools. For clients without remote support, use npx @apify/actors-mcp-server --tools lentic_clockss/stealth-web-scraper with APIFY_TOKEN set.

Example prompt: “Fetch https://example.com, return text and the h1 heading, and use maxResults 1. Wait for the run to succeed before reading the dataset.”

  • Agent execution note: Set maxResults between 5 and 20 on exploratory tool calls to keep responses compact, and read RUN_SUMMARY and INPUT_REPORT from the default key-value store to inspect normalized inputs and delivered row counts. When a long run briefly reports ABORTED with the automatic-resume status message, wait for the same run ID to finish as SUCCEEDED instead of starting a duplicate run.

Apify API

These examples read rows only if the waiting call returns a successful run. If it returns ABORTED with the automatic-resume message, retain its ID and wait for the same run to succeed.

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("lentic_clockss/stealth-web-scraper").call(
run_input={"urls": ["https://example.com"], "maxResults": 1}
)
if run["status"] == "SUCCEEDED":
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)
else:
print(run["id"], run["status"])
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('lentic_clockss/stealth-web-scraper').call({
urls: ['https://example.com'], maxResults: 1
});
if (run.status === 'SUCCEEDED') {
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
} else {
console.log(run.id, run.status);
}

For an asynchronous start, send POST https://api.apify.com/v2/acts/lentic_clockss~stealth-web-scraper/runs with JSON input and an Authorization: Bearer <YOUR_APIFY_TOKEN> header. After success, read GET https://api.apify.com/v2/datasets/<datasetId>/items?format=json.

Integrations

Use the Actor's Integrations tab to connect results to Google Sheets, Make, Zapier, n8n, Slack, Airtable or a webhook. Apify Schedules can repeat the same URL input for change tracking.

FAQ

Public availability does not remove your responsibilities. Check the site's terms and applicable data protection law. Collecting or using personal data requires a lawful basis; only process information you are entitled to use.

Why did my run end as ABORTED?

Check its status message. The automatic-resume message means collection is pending and the same run should resume. A manual abort or another failure is not a promise of automatic continuation.

How many results can I get?

At most one successful row for each of up to 40 unique URLs per run, subject to the cost allowance. Duplicates and failed pages do not add successful rows. Free-plan users have a five-run allowance.

Why are some fields empty?

The page may not show the value, extraction may be ambiguous, or a selector may not match. leadAbsences explains contact gaps. Missing scalar values can be omitted from dataset rows; not_extracted does not prove the site lacks the information.

Why do leadAbsences reasons look different from earlier runs?

From November 2026 the reasons are English codes. Earlier runs returned Chinese-language labels with the same meaning. If you filter on the old labels, switch to the new codes:

Earlier label (JSON-escaped)MeaningNew value
"\u672a\u63d0\u53d6\u5230"Not found in the pagenot_extracted
"\u6765\u6e90\u5b9e\u4f53\u4e0d\u660e\u786e"The page's structured data names several organizations or people and none is the main one (address.street, geo, person, org)not_extracted (source entity is ambiguous)
"\u63d0\u53d6\u5931\u8d25"Contact extraction failed on the fetched page (every entry except address.unit)not_extracted (extraction failed)

Only reason changed. Entries keep their shape: {"reason": ..., "surface": ...} for phones, emails, address.street and geo, where surface is the page URL (html when none is known), and {"reason": ...} for address.unit, person, org and web. None of these codes means the site lacks the value.

Does it find contact pages automatically?

No. Supply the contact page URL yourself. It does not follow contact links or submit contact forms. Contact attributes belong to the supplied page, not a complete company profile.

Yes, when they are present and your CSS selectors match. Put your chosen names in extractSelectors. This does not automatically produce one row per product or review on a listing page.

Am I charged for failed pages?

There is no successful-row charge for a failed page or a missing required selector. Check the failures dataset. The run start fee still applies, and a run with no successful pages can fail.

Why is page content shortened?

Compact row JSON is limited to 250,000 bytes. HTML, text, selector values and contact lists may be shortened. Check the truncation flags; htmlChars and textChars record original lengths when present. Contact lists contain at most 50 entries, with totals when capped.

Limitations

  • Public pages only; login-only dashboards and complex interactive workflows are not supported use cases.
  • Success and country-specific availability vary by page. Test one representative URL before a larger run.
  • No automatic pagination, link crawling or separate product/review rows.
  • No guaranteed phone, email, address or person details for every URL.
  • Localhost, private-network and other non-public addresses are rejected.
  • Large content can be shortened, and resource blocking can affect page completeness.
  • A dedicated site scraper may be more suitable when you need site-specific structured fields.

More developer and data tools from the same publisher:

Browse all of our scrapers at apify.com/lentic_clockss.

Support

Found a problem or need another field? Open an issue in the Issues tab of this Actor's page and include the run ID and the input you used.