Etsy Scraper - Extract Product Data, Prices & Reviews avatar

Etsy Scraper - Extract Product Data, Prices & Reviews

Pricing

from $2.60 / 1,000 results

Go to Apify Store
Etsy Scraper - Extract Product Data, Prices & Reviews

Etsy Scraper - Extract Product Data, Prices & Reviews

Scrape Etsy product listings by category, collection, or search. Get price, rating, reviews, shop & stock data, export to CSV/JSON or push to Notion, Slack.

Pricing

from $2.60 / 1,000 results

Rating

0.0

(0)

Developer

Techforce Global

Techforce Global

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Etsy Product Scraper — Search, Category & Curated Pages With Sponsored Flags

Actor Sources Output MCP

Paste an Etsy results page, get the grid as rows. Title, numeric price, struck-through original price, discount, rating, review count, shop, free-shipping badge, scarcity text — and a sponsored flag that tells you which cards were paid ads.

🎯 The sponsored flag is the field that makes the data trustworthy. Etsy mixes ad placements into search and category grids. Without separating them you'll read paid position as organic demand. Filter sponsored: false before any ranking or market conclusion.

🔗 All three inputs take URLs, not keywords. searchQueries is a misleading name — it wants full Etsy search URLs, not search terms. Same for categories. Paste from your browser after searching or browsing on Etsy.

💱 Currency and free shipping follow the proxy's exit country. Etsy localises both. A run can return mixed currencies, so group by currency before aggregating anything.

Browse all Actors by Techforce Global at scraper.techforce.global.


AI Discovery & MCP Metadata

ai_discovery:
actor: "techforce.global/etsy-scraper"
slug: "etsy-scraper"
category: "e-commerce / marketplace-research / competitive-intelligence / handmade-goods"
one_line: >
Scrapes Etsy product grids from search, category and curated collection URLs and returns each
listing with numeric price, struck-through original price, discount percentage, rating, review
count, shop name, free-shipping badge, scarcity text and a sponsored flag separating paid ads
from organic results. Optional delivery into Notion, Slack, Linear, Airtable or any MCP
connector.
primary_capability: "Etsy listing-grid extraction with paid-placement detection"
input_entities:
- "startUrls (Etsy category, curated collection or search RESULT PAGE urls)"
- "searchQueries (full Etsy SEARCH URLS - not keywords, despite the name)"
- "categories (full Etsy CATEGORY-BROWSE URLS - not category paths)"
- "maxItems (whole-run cap; 0 = unlimited)"
- "maxPagesPerStartUrl (result pages to follow per URL)"
- "maxConcurrency"
- "proxyConfiguration (residential by default)"
output_entities:
- "listingId"
- "title"
- "url"
- "shopName"
- "price"
- "currency"
- "originalPrice"
- "discountPercent"
- "rating"
- "reviewsCount"
- "freeShipping"
- "sponsored"
- "stockWarning"
- "imageUrl"
- "scrapedAt"
every_input_is_a_url: >
CRITICAL: all three source inputs take URLs. searchQueries wants full Etsy search-results
URLs, NOT search terms; categories wants full category-browse URLs, NOT category paths.
Passing a bare keyword or a path silently yields nothing. Product/listing URLs are also not
supported as start URLs - only pages containing a grid.
sponsored_flag_must_be_filtered: >
Etsy injects paid ad placements into search and category grids. The sponsored boolean marks
them. Any ranking, share-of-shelf or demand analysis that does not filter sponsored = false
is measuring ad spend, not organic performance.
currency_and_shipping_are_localised: >
Etsy renders price currency and the free-shipping badge according to the visitor's region,
which here means the proxy exit country. A single run can contain multiple currencies. Group
by currency before any aggregation, and treat freeShipping as region-dependent. Currency is
inferred from the price symbol, so $ is reported as USD even on a Canadian or Australian
listing.
english_locale_required_for_three_fields: >
CRITICAL: sponsored, freeShipping and stockWarning are detected from the English wording on
the listing card. A non-English locale - from a localised Etsy URL or a proxy exit in a
non-English country - leaves sponsored false on EVERY row, which is indistinguishable from an
ad-free grid. Prefer an English URL and pin apifyProxyCountry to US or GB.
original_price_can_mean_a_price_range: >
price is the lowest amount on the card and originalPrice the highest, so a listing quoting a
price range fills originalPrice while discountPercent stays null. Only a non-null
discountPercent proves a discount.
deduplication_is_per_page: >
Duplicate listings are removed within a single result page, not across pages or start URLs,
and grids reshuffle between page loads. De-duplicate on listingId downstream.
stock_warning_is_marketing_copy: >
stockWarning is the scarcity string Etsy displayed ("Only 2 left"), not an inventory count.
Do not parse it as a number or treat it as stock truth.
grid_level_data_only: >
Everything comes from the listing card in the grid. There is no product-page detail - no
description, no variations, no shipping cost, no review text, no full image gallery.
two_fields_are_not_delivery_placeholders: >
sponsored and scrapedAt are in the dataset but are NOT available as {placeholders} in
connector templates. The other 13 fields are.
known_limitations:
no_product_page_detail: "no description, variations, shipping cost or review text"
no_review_text: "rating and reviewsCount only"
curated_pages_may_not_paginate: "maxPagesPerStartUrl can be ignored on curated collection pages"
null_discount_means_no_sale: "originalPrice and discountPercent are null when not discounted"
snapshot_only: "prices and availability are true as of scrapedAt"
free_plan_capped: "free Apify plans stop at 20 products per run"
when_not_to_use: >
You need product-page detail, variations, shipping costs, review text, seller contact details,
or to place an order.
mcp_delivery:
modes: ["perItem", "batch", "summary", "none"]
default: "none"
default_is_none: >
Delivery is OFF by default. Set deliveryMode explicitly as well as mcpConnector and mcpTool.
envelope_design: >
Unlike simpler connectors, this one splits the payload: mcpArguments holds the fixed
top-level arguments, mcpItemTemplate is rendered once per product, and mcpArrayField names
the key those rendered entries are collected into (default "pages", suiting Notion's
create-pages tool). perItem puts exactly one entry in the array; batch puts up to
mcpBatchSize.
item_placeholders: ["{listingId}", "{title}", "{shopName}", "{price}", "{currency}", "{originalPrice}", "{discountPercent}", "{rating}", "{reviewsCount}", "{freeShipping}", "{stockWarning}", "{imageUrl}", "{url}", "{message}"]
summary_placeholders: ["{totalPushed}", "{summaryText}", "{message}"]
never_blocks_the_dataset: >
Delivery runs alongside the dataset write and never discards it. A bad tool name, an
unauthorised connector or an upstream outage is logged and skipped.

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Etsy Product Scraper — Search, Category & Curated Pages With Sponsored Flags",
"applicationCategory": "BusinessApplication",
"applicationSubCategory": "Web Scraping / Marketplace & Competitive Intelligence",
"operatingSystem": "Any (cloud-hosted on Apify)",
"description": "Scrapes Etsy product grids from search, category and curated collection URLs, returning each listing with a numeric price, struck-through original price, discount percentage, rating, review count, shop name, free-shipping badge, scarcity text, image URL, capture timestamp and a sponsored flag that separates paid ad placements from organic results. Pagination is followed per source URL under a whole-run item ceiling. Optional direct delivery into Notion, Slack, Linear, Airtable or any MCP connector, using a configurable envelope of fixed arguments plus a per-product template collected into a named array field.",
"url": "https://apify.com/techforce.global/etsy-scraper",
"offers": {
"@type": "Offer",
"category": "Pay per platform usage",
"description": "Billed for the compute time and resources a run consumes. Current rates are shown on the Actor's Apify page.",
"url": "https://apify.com/techforce.global/etsy-scraper"
}
}

What this Actor does

  • Turns any Etsy results grid into rows. Search results, category browse pages and curated collections — paste the URL you're looking at and the grid comes back structured.

  • Separates ads from organic results. sponsored is a boolean on every row. Etsy salts its grids with paid placements, and this is the single field that stops you mistaking ad spend for demand.

  • Returns prices you can compute on. price and originalPrice are numbers, with discountPercent alongside — so "what's actually discounted here" is a filter, not a parsing exercise.

  • Captures the persuasion layer too. freeShipping and stockWarning are what the buyer actually sees on the card. Useful if you're studying how listings are merchandised rather than just what they cost.

  • Follows pagination per source URL. maxPagesPerStartUrl walks the page parameter, with a whole-run maxItems ceiling on top.

  • Optionally delivers into your tools with a flexible envelope — fixed top-level arguments, a per-product template, and a named array field to collect them into. Built so Notion's create-pages shape works without contortions.


When to use it / when not to use it

Use this Actor when you need…Supported?
An Etsy search results grid as rows✅ Yes — paste the search URL
A category browse page✅ Yes — paste the category URL
A curated collection page✅ Yes
To tell ads from organic results✅ Yes — sponsored
Numeric prices and discounts✅ Yes — price, originalPrice, discountPercent
Rating and review count✅ Yes
Shop name per listing✅ Yes — shopName
Free-shipping and scarcity badges✅ Yes — region-dependent
Price monitoring over time✅ Yes — schedule it and diff on listingId
Results in Notion / Slack / Airtable✅ Yes — MCP delivery
Keywords instead of URLs❌ All three inputs take URLs
Individual product URLs as input❌ Grid pages only
Product description or variations❌ Grid-level data only
Shipping cost (as a number)❌ Only the free-shipping badge
Review text or reviewer names❌ Counts and averages only
Actual inventory countsstockWarning is marketing copy
Seller contact details❌ Not returned
Placing an order❌ Read-only

Execution Rules — Avoid Common Agent Failures

  1. All three source inputs take URLs. searchQueries wants a full Etsy search-results URL, not a keyword — the name is misleading. categories wants a full category-browse URL, not a path like jewelry/necklaces. Paste from your browser after searching or browsing. A bare keyword yields nothing, with no error.

  2. Product URLs are not valid start URLs. Only pages containing a grid — search, category or curated. A single listing URL won't work.

  3. Supply at least one of the three. None is marked required, so an empty input passes validation and scrapes nothing.

  4. Filter sponsored: false before any ranking claim. Etsy mixes paid placements into grids. A "top 20 by position" that includes sponsored rows is a measure of ad spend. This is the most consequential mistake you can make with this dataset.

  5. currency can vary within one run. Etsy localises pricing by visitor region, which here follows the proxy exit country. Group by currency before aggregating — and if you need one currency, pin apifyProxyCountry in proxyConfiguration. Pin an English-speaking country (US, GB) — see the next rule for why.

  6. The grid must come back in English, or three fields go quiet. sponsored, freeShipping and stockWarning are all read from the wording on the card. A non-English locale — from a localised Etsy URL or a proxy exit in a non-English country — leaves sponsored false on every row, which reads exactly like an ad-free grid. If that column is uniformly false, check the locale before you believe it.

  7. freeShipping is region-dependent too. It's what the card showed for that request, not a universal property of the listing.

  8. stockWarning is marketing copy, not inventory. "Only 2 left" is a string Etsy chose to display. Don't parse a number out of it or treat it as stock truth.

  9. discountPercent is what proves a sale — not originalPrice. Null discount fields mean not on sale: a signal, not missing data. But the reverse does not hold. price is the lowest amount on the card and originalPrice the highest, so a listing quoting a price range fills in originalPrice with discountPercent still null. Filter on non-null discountPercent to get actual discounts.

  10. maxItems is a whole-run ceiling; maxPagesPerStartUrl is per URL. Three URLs at 3 pages each is 9 page loads, but maxItems can stop it early. Set both deliberately.

  11. Curated collection pages may ignore pagination. Getting one page from a curated URL despite maxPagesPerStartUrl: 3 is expected, not a bug.

  12. Everything is grid-level. No descriptions, variations, shipping costs, review text or image galleries. Don't build a pipeline expecting more.

  13. deliveryMode defaults to none. Delivery is off until you set it explicitly and provide mcpConnector and mcpTool.

  14. sponsored and scrapedAt are not delivery placeholders. They're in the dataset; the other 13 fields are available in templates.

  15. Free Apify plans stop at 20 products per run. The run succeeds and says so.


Input Contract

Sources — all three take URLs

ParameterTypeRequiredDefaultDescription
startUrlsarraysee rule 3Etsy category, curated or search result page URLs. Product URLs are not supported.
searchQueriesarray<string>see rule 3Full Etsy search URLs — e.g. https://www.etsy.com/search?q=statement+necklace. Not keywords.
categoriesarray<string>see rule 3Full Etsy category URLs — e.g. https://www.etsy.com/c/jewelry/necklaces. Not paths.

Limits and runtime

ParameterTypeDefaultDescription
maxItemsinteger100Whole-run product cap. 0 = unlimited.
maxPagesPerStartUrlinteger3Result pages to follow per URL. 1 = first page only. Curated pages may ignore it.
maxConcurrencyinteger5Parallel requests. Lower if you're being blocked.
proxyConfigurationobjectApify Proxy, residentialLeave enabled. Pin apifyProxyCountry if you need one currency.

Delivery (optional)

ParameterTypeDefaultDescription
mcpConnectorstringMCP connector to deliver into. The Console offers a picker of connectors you've authorised.
mcpToolstring""Tool to call — e.g. notion-create-pages, send_message. Required when a connector is set.
deliveryModestring"none"perItem, batch, summary, none. Off by default.
mcpArgumentsobject{}Fixed top-level arguments — everything except the product list.
mcpItemTemplateobject{}Shape of one product entry, rendered once per product.
mcpArrayFieldstring"pages"Key in mcpArguments that receives the rendered entries. "pages" suits Notion's create-pages tool.
mcpBatchSizeinteger10batch mode only — products per connector call.
mcpMessageTemplatestring""Text rendered into {message}.

The envelope split is unusual and worth understanding: mcpArguments is the wrapper, mcpItemTemplate is the repeated element, and mcpArrayField names where the elements go. In perItem the array holds exactly one entry per call; in batch it holds up to mcpBatchSize.

Delivery placeholders

ScopePlaceholders
Per product (mcpItemTemplate){listingId} {title} {shopName} {price} {currency} {originalPrice} {discountPercent} {rating} {reviewsCount} {freeShipping} {stockWarning} {imageUrl} {url} {message}
Summary mode{totalPushed} {summaryText} {message}

sponsored and scrapedAt are not available as placeholders.


Output Contract

One dataset item per listing card.

FieldTypeNullableMeaning
listingIdstringyesEtsy's listing id. The stable key for joining runs.
titlestringyesListing title from the card.
urlstringyesCanonical listing URL, tracking parameters stripped.
shopNamestringyesSelling shop. Often a sole trader — see Compliance.
pricenumberyesCurrent price, numeric. On a price-range card this is the lowest amount shown.
currencystringyesCurrency as rendered. Can vary within a run — Etsy localises.
originalPricenumberyesStruck-through pre-sale price, or the top of a price range. Null when the card showed a single price.
discountPercentnumberyesDiscount as displayed. Null when not discounted — this, not originalPrice, is the proof of a sale.
ratingnumberyesAverage stars out of 5. Null when unreviewed.
reviewsCountintegeryesReview count. Null or 0 for a new listing.
freeShippingbooleanyesFree-shipping badge. Region-dependent.
sponsoredbooleanyestrue = a paid ad placement. Filter before ranking analysis. Read from the English card text — all-false means the locale, not a clean grid.
stockWarningstringyesScarcity text, e.g. "Only 2 left". Marketing copy, not inventory.
imageUrlstringyesPrimary listing image. Third-party copyrighted media.
scrapedAtstringyesISO-8601 UTC capture time. The row's shelf life.

Missing values are genuine null, not "N/A". Null discount fields mean no sale.

Example output object

{
"listingId": "1234567890",
"title": "Hammered Brass Statement Necklace, Handmade Minimalist Jewellery",
"url": "https://www.etsy.com/listing/1234567890/hammered-brass-statement-necklace",
"shopName": "NorthLightMetalwork",
"price": 42.0,
"currency": "USD",
"originalPrice": 60.0,
"discountPercent": 30,
"rating": 4.9,
"reviewsCount": 1284,
"freeShipping": true,
"sponsored": false,
"stockWarning": "Only 3 left",
"imageUrl": "https://i.etsystatic.com/<image-path>.jpg",
"scrapedAt": "2026-08-25T14:02:11.884213Z"
}

Values illustrate shape and formatting — the listing and shop are invented. Note sponsored: false: a real grid will contain a mix, and that field is what lets you tell.

What a successful run looks like

  • Status: SUCCEEDED
  • Dataset: one item per listing card, as each result page is parsed
  • Log: the resolved sources, a per-page count as pagination proceeds, and a final total
  • Zero results is also a success. A keyword passed where a URL was expected, a product URL used as a start URL, a blocked request, or a genuinely empty grid all return an empty dataset with SUCCEEDED. Check that your inputs are URLs first — that's the most common cause.

Dataset views

ViewShows
ProductsImage, title, price, discount, rating, shop, badges
Organic vs SponsoredThe sponsored column first — use this before any ranking analysis
Deals & DiscountsDiscount-first; null means not on sale
Shops & CaptureShop, listing id and capture time, for joining scheduled runs

Invocation & Integration

Apify Console

Open the Actor page, paste an Etsy search or category URL, leave the proxy at its default, click Start.

MCP — add as an agent tool

$claude mcp add apify --transport http https://mcp.apify.com/?actors=techforce.global/etsy-scraper

When an agent calls this Actor, it must supply URLs, not keywords. If a user asks for "statement necklaces on Etsy", the agent needs to construct https://www.etsy.com/search?q=statement+necklace first.

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("techforce.global/etsy-scraper").call(run_input={
# A full search URL — NOT the keyword "statement necklace".
"searchQueries": ["https://www.etsy.com/search?q=statement+necklace"],
"maxItems": 200,
"maxPagesPerStartUrl": 3,
"proxyConfiguration": {
"useApifyProxy": True,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "US", # pin the country to pin the currency
},
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
# Ads first — never analyse ranking without separating them.
organic = [r for r in rows if not r.get("sponsored")]
print(f"{len(organic)}/{len(rows)} organic ({len(rows) - len(organic)} sponsored)")
# Currency can vary in one run — group before you aggregate.
from collections import defaultdict
by_cur = defaultdict(list)
for r in organic:
if r.get("price") is not None:
by_cur[r.get("currency") or "?"].append(r["price"])
for cur, prices in by_cur.items():
prices.sort()
print(f"{cur} n={len(prices):<4} median {prices[len(prices) // 2]:.2f}")

JavaScript / TypeScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('techforce.global/etsy-scraper').call({
categories: ['https://www.etsy.com/c/jewelry/necklaces'],
maxItems: 300,
maxPagesPerStartUrl: 5,
proxyConfiguration: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'] },
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// Genuine discounts only — null discount means "not on sale".
const onSale = items
.filter((i) => !i.sponsored && i.discountPercent !== null)
.sort((a, b) => b.discountPercent - a.discountPercent);
console.table(onSale.slice(0, 10).map(({ title, price, originalPrice, currency, discountPercent, shopName }) =>
({ title: title?.slice(0, 40), was: `${currency} ${originalPrice}`, now: `${currency} ${price}`, off: `${discountPercent}%`, shopName })));

cURL — synchronous (waits, returns the dataset)

curl -X POST "https://api.apify.com/v2/acts/techforce.global~etsy-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H 'Content-Type: application/json' \
-d '{
"searchQueries": ["https://www.etsy.com/search?q=ceramic+mug"],
"maxItems": 50,
"maxPagesPerStartUrl": 2,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}'

cURL — asynchronous with a spend cap

curl -X POST "https://api.apify.com/v2/acts/techforce.global~etsy-scraper/runs?token=<YOUR_APIFY_TOKEN>&maxTotalChargeUsd=3" \
-H 'Content-Type: application/json' \
-d '{ "categories": ["https://www.etsy.com/c/jewelry"], "maxItems": 0, "maxPagesPerStartUrl": 10 }'

maxItems: 0 is unlimited — maxTotalChargeUsd is then your only ceiling.

Competitive analysis — the configuration that answers the question

{
"searchQueries": ["https://www.etsy.com/search?q=personalised+dog+collar"],
"maxItems": 300,
"maxPagesPerStartUrl": 5,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "US"
}
}

Pinning the country pins the currency, so prices are comparable. Then filter sponsored: false and group by shopName to see who actually owns the organic grid.

With MCP delivery — a Notion database of listings

{
"searchQueries": ["https://www.etsy.com/search?q=ceramic+mug"],
"maxItems": 100,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] },
"mcpConnector": "notion",
"mcpTool": "notion-create-pages",
"deliveryMode": "batch",
"mcpBatchSize": 10,
"mcpArrayField": "pages",
"mcpArguments": {
"parent": { "database_id": "<YOUR_NOTION_DATABASE_ID>" }
},
"mcpItemTemplate": {
"properties": {
"Name": "{title}",
"Shop": "{shopName}",
"Price": "{price}",
"Rating": "{rating}",
"Link": "{url}"
}
}
}

mcpArguments is the wrapper, mcpItemTemplate is one row, mcpArrayField collects them. batch with mcpBatchSize: 10 is ten products per call — far fewer calls than perItem.

Scheduling for price monitoring

Put a fixed input on a schedule and join runs on listingId. Because originalPrice is captured too, you can tell a genuine price drop from a changed "was" price — and because sponsored is captured, you can track whether a competitor started buying ads.


Autonomous Agent Error Handling & Resolution Matrix

Each row is a condition observable from the run's status, log or dataset.

ConditionWhat you seeCauseDeterministic action
NO_SOURCESSUCCEEDED, empty datasetAll three source inputs emptyModify Input: supply at least one URL.
KEYWORD_PASSED_AS_URLSUCCEEDED, empty datasetA bare keyword given to searchQueries, or a path to categoriesModify Input: use a full Etsy URL. This is the most common cause of an empty run.
PRODUCT_URL_AS_START_URLNothing from that URLListing URLs aren't supportedModify Input: use a search, category or curated grid page.
ZERO_RESULTSSUCCEEDED, empty datasetAny of the above, blocking, or a genuinely empty gridCheck your inputs are URLs first, then enable/upgrade the proxy and retry once.
MIXED_CURRENCIEScurrency differs across rowsEtsy localises by regionExpected. Group by currency, or pin apifyProxyCountry.
SPONSORED_SKEWING_ANALYSISRankings look implausiblePaid placements counted as organicFix Consumer: filter sponsored: false.
NULL_DISCOUNT_FIELDSoriginalPrice / discountPercent nullThe listing isn't on saleExpected — a signal. Filter non-null discountPercent for deals.
PRICE_RANGE_LOOKS_LIKE_A_SALEoriginalPrice set, discountPercent nullThe card quoted a price range, not a discountFix Consumer: require non-null discountPercent before calling anything a sale.
SPONSORED_ALWAYS_FALSEEvery row has sponsored: falseThe grid came back in a non-English localeModify Input: use an English Etsy URL, or pin apifyProxyCountry to US/GB. freeShipping and stockWarning are affected too.
DUPLICATE_LISTINGSThe same listingId twiceDe-duplication is per page, and grids reshuffle between page loadsFix Consumer: de-duplicate on listingId.
START_URLS_UNVISITEDLater source URLs never appear in the logThe run's request budget is derived from maxItemsModify Input: raise maxItems, or split the URLs across runs.
NULL_PRICEprice is nullThe card showed no priceExpected. Null-check before arithmetic.
FEWER_PAGES_THAN_REQUESTEDFewer pages walked than maxPagesPerStartUrlGrid ran out, or a curated page ignored paginationExpected. Not a failure.
MAX_ITEMS_TRUNCATEDFewer rows than pages × cardsmaxItems bound firstModify Input: raise maxItems.
STOCK_WARNING_NOT_A_NUMBERstockWarning won't parseIt's marketing copy by designFix Expectation: treat it as a string.
LIKELY_BLOCKEDEmpty or thin dataset with the proxy onConcurrency too high, or transient blockModify Input: maxConcurrency: 2 and retry once.
FREE_PLAN_CAPPED20 products; log says soFree Apify planUpgrade the plan.
CONNECTOR_NOT_DELIVERINGConnector set, nothing delivered, no errordeliveryMode still none (the default)Modify Input: set it explicitly.
CONNECTOR_ARRAY_FIELD_WRONGConnector rejects the payloadmcpArrayField doesn't match the tool's expected keyModify Input: set it to the key the tool wants (pages for Notion create-pages).
CONNECTOR_TOOL_NOT_FOUNDLog: tool not available, followed by the available toolsmcpTool name is wrongModify Input: copy an exact name from that log line. Dataset unaffected.
CONNECTOR_UNREACHABLELog: could not reach the connectorConnector not authorised, or running outside the Apify platformAuthorise it in Apify, or set deliveryMode: "none". Data already saved.

Verify any platform-level HTTP status behaviour against the Apify API reference before relying on it in automation — the rows above describe this Actor's observable behaviour, not Apify's transport layer.


Natural Language → Actor Mapping

Every row below assumes the caller builds the Etsy URL first.

A user says…IntentInput
"Statement necklaces on Etsy"Search grid{"searchQueries":["https://www.etsy.com/search?q=statement+necklace"]}
"Everything in the necklaces category"Category{"categories":["https://www.etsy.com/c/jewelry/necklaces"]}
"Scrape this Etsy page"Paste-through{"startUrls":[{"url":"<the-url>"}]}
"Who ranks organically for X?"RankingSearch URL + maxItems: 300, then filter sponsored: false
"How much do ceramic mugs sell for?"Price benchmarkSearch URL + pin apifyProxyCountry, then group by currency
"Best discounts right now"DealsSearch URL + maxItems: 300, then sort discountPercent
"Which shops dominate this niche?"Shop shareSearch URL, filter organic, group by shopName
"Are competitors running ads here?"Ad presenceSearch URL, then count sponsored: true
"Track this category's prices weekly"Scheduled monitorCategory URL on a schedule; join on listingId
"Just a quick sample"Cheap probe{"searchQueries":["<url>"],"maxItems":10,"maxPagesPerStartUrl":1}
"It returned nothing"Wrong input typeCheck you passed a URL, not a keyword
"Put these listings in Notion"Scrape + deliverAbove, plus the delivery envelope

Should NOT route here: bare keywords — build the search URL first · individual product URLs · product descriptions or variations · shipping costs as numbers · review text or reviewer names · real inventory counts · seller contact details · placing an order · Amazon or other marketplaces (see the Amazon and Flipkart Actors).


Agent Execution & Routing Logic

Does the user want ETSY listing data?
├── NO → not this Actor.
│ Amazon → Amazon Product Scraper
│ Flipkart → Flipkart Search Scraper
│ descriptions/variations → not returned (grid data only)
│ placing an order → read-only Actor
└── YES
├── FIRST: do you have a URL, or a keyword?
│ ├── keyword → BUILD THE URL:
│ │ https://www.etsy.com/search?q=<url-encoded terms>
│ │ then pass it in searchQueries
│ ├── category → build https://www.etsy.com/c/<path>, pass in categories
│ ├── a pasted grid URL → startUrls
│ └── a PRODUCT url → NOT SUPPORTED. Find its category or search instead.
├── ALWAYS keep proxyConfiguration at residential
│ └── need one currency? → pin apifyProxyCountry
├── How much?
│ ├── a probe → maxItems 10, maxPagesPerStartUrl 1
│ ├── normal → maxItems 100-300, pages 3-5
│ └── deep → maxItems 0 + ALWAYS a spend cap
├── What is the actual question?
│ ├── organic ranking → FILTER sponsored == false ← non-negotiable
│ ├── price benchmark → pin country, GROUP BY currency
│ ├── deals → filter discountPercent != null, sort desc
│ ├── shop share → filter organic, group by shopName
│ ├── ad presence → count sponsored == true
│ └── change over time → SCHEDULE it; join on listingId
└── Should results land in another app?
├── yes → deliveryMode (DEFAULT none — set it!) + the envelope:
│ mcpArguments = fixed wrapper
│ mcpItemTemplate = one product
│ mcpArrayField = where entries go ("pages" for Notion)
│ batch + mcpBatchSize → far fewer calls than perItem
└── no → leave mcpConnector empty; dataset only

Cost Control & Pricing Transparency

This Actor bills for platform usage — the compute time and resources a run consumes — not per row. Current rates are on the Actor's Apify page, and the Console shows the live total while a run is in progress.

ChoiceEffect on cost
maxItemsThe direct ceiling on how much gets collected.
maxPagesPerStartUrl × number of URLsThe number of page loads — the real driver of run time.
maxConcurrencyHigher finishes sooner if you're not blocked. Being blocked makes it slower.
Passing keywords instead of URLsPure waste — the run costs time and returns nothing. Check this first.
Being blockedRetries cost run time and return nothing. Keep the residential proxy on.

Keeping runs predictable:

  1. First run: one URL, maxItems: 10, maxPagesPerStartUrl: 1. Confirm your input really is a URL and the grid parses.
  2. Verify the input type before scaling. The single most common wasted run here is a keyword passed where a URL belongs — it fails silently.
  3. Pin apifyProxyCountry if you'll aggregate prices. Otherwise you may pay for a dataset you can't safely average.
  4. Always set a spend cap on unattended runs, mandatory with maxItems: 0.
  5. Free Apify plans get 20 products per run — a connectivity check.

Companion machine-readable files

FileWhat it defines
.actor/input_schema.jsonEvery input, its type, default and bounds
.actor/dataset_schema.jsonAll 15 output fields with types, and the four Console views
.actor/output_schema.jsonWhere the run's output is exposed
.actor/actor.jsonActor identity and storage wiring

If this page and the schema files disagree, the schema files win. They are what the platform validates against at run time; this page is documentation and can lag a release.


Troubleshooting

#Symptom you seeMost likely causeFix
1SUCCEEDED, empty datasetA bare keyword was passed to searchQueries, or a path to categoriesPass a full Etsy URL. All three source inputs take URLs, and a keyword fails silently. Check this first — it is the most common cause by a wide margin.
2One source URL returned nothingIt was a product/listing URLOnly grid pages work — search, category or curated. Use the listing's category instead.
3SUCCEEDED, empty dataset, no sources in the logAll three source inputs were left emptyNone is marked required, so an empty input passes validation. Supply at least one.
4Empty or very thin dataset, 403s in the logEtsy rejects datacenter IPs, usually on the first requestKeep proxyConfiguration on the default residential group. Residential proxies need a paid Apify plan — on a free plan this is the likely blocker.
5sponsored is false on every single rowThe grid was served in a non-English localePaid placements are recognised from the English card text. Use an English Etsy URL (/en-us/…) or pin apifyProxyCountry to US or GB. A uniformly false column is the tell — real grids always contain a mix.
6freeShipping always false, stockWarning always nullSame cause as row 5These badges are read from English card text too. Fix the locale and they return.
7originalPrice is set but discountPercent is nullThe card showed a price range, not a discountThis is a range, not a sale: price is the lowest amount on the card and originalPrice the highest. Treat a discount as real only when discountPercent is non-null.
8currency says USD for a listing you know is CAD or AUDCurrency is inferred from the symbol, and $ is assumed to be USDPin apifyProxyCountry so you know which $ you are looking at, and don't mix $ rows from different runs.
9currency differs between rows in one runEtsy localises pricing by visitor regionExpected. Group by currency before aggregating, or pin apifyProxyCountry (see rows 5 and 8 before choosing one).
10The same listing appears twiceDuplicates are removed within a page, not across pages or start URLsDe-duplicate on listingId — the same product legitimately appears on page 1 and page 2 as the grid reshuffles.
11Fewer pages than maxPagesPerStartUrlThe grid ran out, a curated page ignored pagination, or a page returned zero cardsExpected — pagination stops at the first page with no cards. Not a failure.
12Fewer rows than pages x cardsmaxItems bound firstRaise maxItems, or set it to 0 for unlimited and set a spend cap.
13Exactly 20 productsFree Apify plan capThe run still succeeds and says so in the log. Upgrade for unlimited results.
14Later start URLs were never visitedThe whole-run request budget is derived from maxItemsA small maxItems with many source URLs exhausts it early. Raise maxItems, or split the URLs across runs.
15price is nullThe card quoted no priceExpected. Null-check before any arithmetic — null never means free.
16rating and reviewsCount are nullThe listing has no reviews yetExpected on new listings. Treat null as "unrated", not as zero stars.
17Connector configured, nothing delivereddeliveryMode is still nonethe defaultSet it explicitly, and set mcpTool as well.
18Connector rejects the payloadmcpArrayField does not match the key the tool expectsSet it to the tool's key — pages for Notion's create-pages.
19Log: tool not available, followed by a listmcpTool is misspelledCopy an exact name from that log line. The dataset is unaffected.
20Log: could not reach the connectorNot authorised, or running locally rather than on the platformAuthorise it in Apify and run on the platform. Your data is already saved either way.

Diagnostic checklist before opening an issue

  1. Confirm every source value is a URL, not a keyword or a path. This single check explains most empty runs, and it produces no error message of any kind.
  2. Check the proxy. Etsy blocks datacenter IPs almost immediately; residential access requires a paid plan.
  3. Probe cheap: {"searchQueries":["<one search URL>"],"maxItems":10,"maxPagesPerStartUrl":1}. If that returns rows, the pipeline works and your inputs are the problem.
  4. Scan the sponsored column before trusting any analysis. All-false means the locale defeated the detection, not that the grid was ad-free.
  5. Re-read scrapedAt before reporting a price as wrong — this is a snapshot of a marketplace that moves continuously.

If it survives all five, open an Issues ticket with the run ID, the exact input JSON, and what you expected.


FAQ

Setup & scope

Do I need an Etsy account or API key?

No. The Actor reads public listing grids. You need an Apify account and API token only.

Is this an official Etsy API?

No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Etsy. Etsy operates its own Open API for sanctioned access.

Do I need a paid Apify plan?

In practice, yes. Etsy rejects datacenter IPs almost immediately, so the Actor defaults to the residential proxy group — and residential proxies require a paid plan. Free plans are also capped at 20 products per run.

Can I plug it into Claude, Cursor, or a LangChain agent?

Yes — it is a native MCP tool:

claude mcp add apify --transport http https://mcp.apify.com/?actors=techforce.global/etsy-scraper

Input

Why does searchQueries not accept search terms?

Because the name is misleading — it wants a full Etsy search-results URL. Auto-building search URLs from keywords produced pages Etsy blocked, so the Actor now takes the URL you actually browsed. Search on Etsy, then copy the address bar.

What happens if I pass a keyword anyway?

Nothing visible. The run succeeds with an empty dataset and no error. It is the single most common cause of a confusing result here.

Can I scrape an individual product page?

No. Only pages containing a grid — search, category or curated collection.

Is maxItems per URL or per run?

Per run. maxPagesPerStartUrl is the per-URL control. Three URLs at three pages each is nine page loads, but maxItems can stop the run before they finish.

Why did later start URLs never get visited?

The run's total request budget is derived from maxItems, so a small cap with many source URLs can exhaust it before the last URL is reached. Raise maxItems or split the URLs across runs.

Output & data

What does the sponsored flag actually do for me?

It separates paid ad placements from organic results. Etsy salts its grids with ads, so any ranking, share-of-shelf or demand conclusion drawn without filtering sponsored: false is measuring ad spend rather than performance.

Why is sponsored false on every row?

Because the grid came back in a non-English locale. Paid placements — along with the free-shipping badge and the scarcity text — are recognised from the English card text. Use an English Etsy URL or pin apifyProxyCountry to US or GB. An all-false column is a red flag, not a clean grid.

originalPrice is filled in but discountPercent is null. Is it on sale?

No. That combination means the card showed a price rangeprice is the lowest amount found and originalPrice the highest. A discount is real only when discountPercent is non-null.

Can I trust currency?

Within a locale, yes. It is inferred from the price symbol, so , £ and are unambiguous while $ is assumed to be USD — a Canadian or Australian listing will also say USD. Pin the proxy country if that distinction matters.

Why does one run contain several currencies?

Etsy localises prices by visitor region, which here follows the proxy exit country. Group by currency before aggregating anything.

Is stockWarning a real inventory count?

No. It is the scarcity string Etsy chose to display, such as "Only 2 left". Don't parse a number out of it and don't repeat it as fact.

Why do I have the same listing twice?

Duplicates are removed within a single page, not across pages or start URLs, and grids reshuffle between page loads. De-duplicate on listingId.

Do I get product descriptions, variations or shipping costs?

No. Everything comes from the listing card in the grid — no product-page detail, no variations, no shipping cost, no review text and no image gallery.

Are missing values null or "N/A"?

Genuine null. And null carries meaning here: a null price means none was quoted, and null discount fields mean the listing simply isn't on sale.

Cost & runtime

How is this billed?

By platform usage — the compute time and resources a run consumes — not per row. Current rates are on the Actor's Apify page, and the Console shows a running total during a run.

What actually drives the cost?

Page loads, so maxPagesPerStartUrl x the number of source URLs. The most expensive run is the one that returns nothing because a keyword was passed where a URL belonged.

What does the free plan give me?

20 products per run — enough to confirm your inputs work, not enough for a data pull. Residential proxy access, which this Actor needs, is also a paid feature.

How do I guarantee an agent never overspends?

Pass maxTotalChargeUsd as a query parameter on the run endpoint. It is a hard per-execution ceiling, and it is mandatory in spirit whenever maxItems: 0.


ActorBest for
This ActorEtsy grids with paid-placement detection and sale pricing
Amazon Product ScraperAmazon across 7 marketplaces, with search rank and product-page detail
Flipkart Search ScraperFlipkart search results with discount and rating data
Website Contact ScraperBusiness contact details from a seller's own site
Website Content CrawlerWhole-site text, markdown or HTML for RAG and search

Three marketplace Actors, three different questions. Etsy is the one where paid placements are flagged, so it's the one to reach for when organic-versus-ad separation matters.

Browse the full catalogue at scraper.techforce.global.


Compliance & Data Privacy

  • Public listing pages only. Everything returned is published on a public Etsy grid with no login and no paywall.

  • Etsy is different from Amazon here: most sellers are individuals. shopName is a trading name, but on Etsy it very often belongs to a sole trader or a one-person business, and shop names frequently contain the maker's actual name. Where that's the case, shopName is personal data under GDPR / UK GDPR and India's DPDP Act 2023, and aggregating it at scale — building a ranked table of named makers and their prices — is processing you need a basis for. This is the main way this Actor differs from the marketplace scrapers in the same family.

  • No buyer or reviewer data is collected. No review text, no reviewer names, no customer information — only rating averages and counts.

  • Etsy's Terms of Use prohibit automated collection. Scraping and automated access are disallowed without permission. Etsy also operates an official Open API for sanctioned access, which is the route if your use case fits it. This Actor does not remove that risk — it is a risk you accept by running it.

  • Republishing prices carries consumer-protection duties, and Etsy's localisation makes it worse. A price is true only as of scrapedAt and only for the region the run resolved to. Presenting a scraped price as "the" price — when a buyer elsewhere sees a different currency and a different shipping badge — risks misleading-advertising exposure under the UK CPRs, Australian Consumer Law and the US FTC Act. Publish the capture time and the currency.

  • Treat discountPercent and stockWarning sceptically before repeating them. discountPercent is calculated against a seller-set "was" price, and stockWarning is scarcity copy rather than inventory truth. Repeating either as a verified fact is a claim you can't support.

  • Competitive monitoring is normal; price coordination is not. Watching public prices is standard. Using shared data to align pricing with competitors is antitrust exposure under EU Article 101, the UK Competition Act and the US Sherman Act. Keep monitoring internal.

  • Images are the makers' own work. imageUrl points at a photograph a small seller took of something they made. A URL is not a licence — and on a handmade marketplace, reusing listing photography is a live copyright and design-appropriation issue rather than a theoretical one.

  • Attribution over mirroring. url is on every row. Link back rather than mirroring listings, and don't present another maker's products as your own inventory.

  • Respect the load you generate. Keep maxItems, maxPagesPerStartUrl and maxConcurrency to what your question needs.


🆘 Support & Custom Pipeline Engineering

Need automated multi-city scheduled feeds, webhook enrichment, or enterprise data warehouse syncs?



🏷️ Structured data for search & AI discovery

SoftwareApplication describes the Actor; FAQPage wraps the FAQ above so those answers are eligible for rich results, matching it question-for-question.

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Etsy Product Scraper — Search, Category & Curated Pages With Sponsored Flags",
"applicationCategory": "BusinessApplication",
"applicationSubCategory": "Web Scraping / Marketplace & Competitive Intelligence",
"operatingSystem": "Any (cloud-hosted on Apify)",
"url": "https://apify.com/techforce.global/etsy-scraper",
"description": "Scrapes Etsy product grids from search, category and curated collection URLs, returning each listing with a numeric price, struck-through original price, discount percentage, rating, review count, shop name, free-shipping badge, scarcity text, image URL, capture timestamp and a sponsored flag that separates paid ad placements from organic results. Pagination is followed per source URL under a whole-run item ceiling. Optional direct delivery into Notion, Slack, Linear, Airtable or any MCP connector, using a configurable envelope of fixed arguments plus a per-product template collected into a named array field.",
"author": {
"@type": "Organization",
"name": "Techforce Global",
"url": "https://www.techforceglobal.com"
},
"provider": {
"@type": "Organization",
"name": "Apify",
"url": "https://apify.com"
},
"offers": {
"@type": "Offer",
"category": "Pay per platform usage",
"description": "Billed for the compute time and resources a run consumes. Current rates are shown on the Actor's Apify page.",
"url": "https://apify.com/techforce.global/etsy-scraper"
},
"featureList": [
"Search, category and curated collection grids from pasted URLs",
"Sponsored flag separating paid ad placements from organic results",
"Numeric price plus struck-through original price and discount percentage",
"Rating and review count per listing",
"Free-shipping badge and scarcity text as the buyer sees them",
"Shop name per listing for seller-level aggregation",
"Per-row capture timestamp for auditable, diffable snapshots",
"Pagination per source URL under a whole-run item ceiling",
"Flexible MCP delivery envelope with per-product templates and batching",
"Four Console dataset views including organic-versus-sponsored"
],
"isAccessibleForFree": false,
"softwareRequirements": "Apify account and API token; a paid plan for residential proxies",
"keywords": "Etsy scraper, Etsy product data, handmade marketplace, sponsored listings, ad detection, price monitoring, competitive intelligence, discount tracking, shop analysis, MCP connector"
}
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Do I need an Etsy account or API key?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. The Actor reads public listing grids. You need an Apify account and API token only."
}
},
{
"@type": "Question",
"name": "Is this an official Etsy API?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Etsy. Etsy operates its own Open API for sanctioned access."
}
},
{
"@type": "Question",
"name": "Do I need a paid Apify plan?",
"acceptedAnswer": {
"@type": "Answer",
"text": "In practice, yes. Etsy rejects datacenter IPs almost immediately, so the Actor defaults to the residential proxy group - and residential proxies require a paid plan. Free plans are also capped at 20 products per run."
}
},
{
"@type": "Question",
"name": "Can I plug it into Claude, Cursor, or a LangChain agent?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes - it is a native MCP tool: claude mcp add apify --transport http https://mcp.apify.com/?actors=techforce.global/etsy-scraper"
}
},
{
"@type": "Question",
"name": "Why does searchQueries not accept search terms?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Because the name is misleading - it wants a full Etsy search-results URL. Auto-building search URLs from keywords produced pages Etsy blocked, so the Actor now takes the URL you actually browsed. Search on Etsy, then copy the address bar."
}
},
{
"@type": "Question",
"name": "What happens if I pass a keyword anyway?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Nothing visible. The run succeeds with an empty dataset and no error. It is the single most common cause of a confusing result here."
}
},
{
"@type": "Question",
"name": "Can I scrape an individual product page?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Only pages containing a grid - search, category or curated collection."
}
},
{
"@type": "Question",
"name": "Is maxItems per URL or per run?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Per run. maxPagesPerStartUrl is the per-URL control. Three URLs at three pages each is nine page loads, but maxItems can stop the run before they finish."
}
},
{
"@type": "Question",
"name": "Why did later start URLs never get visited?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The run's total request budget is derived from maxItems, so a small cap with many source URLs can exhaust it before the last URL is reached. Raise maxItems or split the URLs across runs."
}
},
{
"@type": "Question",
"name": "What does the sponsored flag actually do for me?",
"acceptedAnswer": {
"@type": "Answer",
"text": "It separates paid ad placements from organic results. Etsy salts its grids with ads, so any ranking, share-of-shelf or demand conclusion drawn without filtering sponsored: false is measuring ad spend rather than performance."
}
},
{
"@type": "Question",
"name": "Why is sponsored false on every row?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Because the grid came back in a non-English locale. Paid placements - along with the free-shipping badge and the scarcity text - are recognised from the English card text. Use an English Etsy URL or pin apifyProxyCountry to US or GB. An all-false column is a red flag, not a clean grid."
}
},
{
"@type": "Question",
"name": "originalPrice is filled in but discountPercent is null. Is it on sale?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. That combination means the card showed a price range - price is the lowest amount found and originalPrice the highest. A discount is real only when discountPercent is non-null."
}
},
{
"@type": "Question",
"name": "Can I trust currency?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Within a locale, yes. It is inferred from the price symbol, so , and are unambiguous while $ is assumed to be USD - a Canadian or Australian listing will also say USD. Pin the proxy country if that distinction matters."
}
},
{
"@type": "Question",
"name": "Why does one run contain several currencies?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Etsy localises prices by visitor region, which here follows the proxy exit country. Group by currency before aggregating anything."
}
},
{
"@type": "Question",
"name": "Is stockWarning a real inventory count?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. It is the scarcity string Etsy chose to display, such as \"Only 2 left\". Don't parse a number out of it and don't repeat it as fact."
}
},
{
"@type": "Question",
"name": "Why do I have the same listing twice?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Duplicates are removed within a single page, not across pages or start URLs, and grids reshuffle between page loads. De-duplicate on listingId."
}
},
{
"@type": "Question",
"name": "Do I get product descriptions, variations or shipping costs?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Everything comes from the listing card in the grid - no product-page detail, no variations, no shipping cost, no review text and no image gallery."
}
},
{
"@type": "Question",
"name": "Are missing values null or \"N/A\"?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Genuine null. And null carries meaning here: a null price means none was quoted, and null discount fields mean the listing simply isn't on sale."
}
},
{
"@type": "Question",
"name": "How is this billed?",
"acceptedAnswer": {
"@type": "Answer",
"text": "By platform usage - the compute time and resources a run consumes - not per row. Current rates are on the Actor's Apify page, and the Console shows a running total during a run."
}
},
{
"@type": "Question",
"name": "What actually drives the cost?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Page loads, so maxPagesPerStartUrl x the number of source URLs. The most expensive run is the one that returns nothing because a keyword was passed where a URL belonged."
}
},
{
"@type": "Question",
"name": "What does the free plan give me?",
"acceptedAnswer": {
"@type": "Answer",
"text": "20 products per run - enough to confirm your inputs work, not enough for a data pull. Residential proxy access, which this Actor needs, is also a paid feature."
}
},
{
"@type": "Question",
"name": "How do I guarantee an agent never overspends?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Pass maxTotalChargeUsd as a query parameter on the run endpoint. It is a hard per-execution ceiling, and it is mandatory in spirit whenever maxItems: 0."
}
}
]
}