{
  "data": {
    "id": "I2vzZsFaC2LGvOXoY",
    "actId": "B47SmyQ83rDLAqzH1",
    "userId": "IYgBDiXtHOijAaUel",
    "startedAt": "2026-10-31T05:58:01.274Z",
    "finishedAt": "2026-10-31T05:58:09.774Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 8413,
      "runTimeSecs": 8.413,
      "computeUnits": 0.009347777777777778,
      "imageSizeBytes": 358815744
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "QYo53EZkJk0prePWY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"Etsy Scraper Input\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"startUrls\": {\n      \"title\": \"Etsy URLs\",\n      \"type\": \"array\",\n      \"description\": \"Etsy market URLs such as https://www.etsy.com/market/handmade_jewelry are the reliable route. Category URLs such as https://www.etsy.com/c/jewelry also work and expose more cards per page. Legacy /search?q= URLs are converted to the matching market URL automatically. Shop and individual listing URLs are usually blocked by Etsy's DataDome protection; they are accepted for backward compatibility and reported as blocked instead of silently returning nothing.\",\n      \"editor\": \"requestListSources\"\n    },\n    \"searchQueries\": {\n      \"title\": \"Search Queries\",\n      \"type\": \"array\",\n      \"description\": \"Keywords to search on Etsy. Each query is mapped to Etsy's market page (https://www.etsy.com/market/<slug>), the route that works without a browser or login. If Etsy redirects a keyword to a category page, the actor follows that category route. Each query is scraped as a separate task.\",\n      \"editor\": \"stringList\",\n      \"prefill\": [\n        \"handmade jewelry\"\n      ]\n    },\n    \"maxItems\": {\n      \"title\": \"Max Items\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum number of listings to collect per URL or search query. A keyword market exposes at most 25 pages (about 200 listings); category URLs can expose more pages. Larger values return what Etsy exposes and report a partial result instead of pretending the request was complete.\",\n      \"default\": 100,\n      \"prefill\": 1,\n      \"minimum\": 1,\n      \"maximum\": 1000,\n      \"editor\": \"number\"\n    },\n    \"includeDetails\": {\n      \"title\": \"Include Listing Details\",\n      \"type\": \"boolean\",\n      \"description\": \"Attempts to open each listing page for description, materials, tags, variations, images, and seller details. Etsy often blocks listing pages with DataDome. When that happens, rows are still returned with detailsStatus set to 'blocked' and the unavailable detail fields omitted; they are never filled with placeholders.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"sortBy\": {\n      \"title\": \"Sort results by\",\n      \"type\": \"string\",\n      \"description\": \"Server-side Etsy ordering for market and category pages.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"\",\n        \"most_relevant\",\n        \"price_asc\",\n        \"price_desc\",\n        \"date_desc\",\n        \"highest_reviews\"\n      ],\n      \"enumTitles\": [\n        \"Most relevant\",\n        \"Price: low to high\",\n        \"Price: high to low\",\n        \"Newest\",\n        \"Most reviewed\"\n      ],\n      \"default\": \"\",\n      \"prefill\": \"\"\n    },\n    \"minPrice\": {\n      \"title\": \"Minimum price\",\n      \"type\": \"number\",\n      \"description\": \"Drop listings below this price in the selected market currency. Etsy does not expose a reliable server-side price filter on market pages, so this is applied to fetched cards. 0 disables the lower bound.\",\n      \"minimum\": 0,\n      \"maximum\": 1000000,\n      \"default\": 0,\n      \"prefill\": 0,\n      \"editor\": \"number\"\n    },\n    \"maxPrice\": {\n      \"title\": \"Maximum price\",\n      \"type\": \"number\",\n      \"description\": \"Drop listings above this price in the selected market currency. 0 disables the upper bound.\",\n      \"minimum\": 0,\n      \"maximum\": 1000000,\n      \"default\": 0,\n      \"prefill\": 0,\n      \"editor\": \"number\"\n    },\n    \"freeShippingOnly\": {\n      \"title\": \"Free shipping only\",\n      \"type\": \"boolean\",\n      \"description\": \"Use Etsy's free-shipping market filter.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"starSellerOnly\": {\n      \"title\": \"Star Seller only\",\n      \"type\": \"boolean\",\n      \"description\": \"Use Etsy's Star Seller market filter.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"country\": {\n      \"title\": \"Country / Market\",\n      \"type\": \"string\",\n      \"description\": \"Target Etsy market. Sets the market URL locale and request language, so prices and ranking match the selected region (for example Germany returns EUR and Japan returns JPY).\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"US\",\n        \"DE\",\n        \"GB\",\n        \"FR\",\n        \"AU\",\n        \"CA\",\n        \"IT\",\n        \"ES\",\n        \"NL\",\n        \"JP\"\n      ],\n      \"enumTitles\": [\n        \"United States (USD)\",\n        \"Germany (EUR)\",\n        \"United Kingdom (GBP)\",\n        \"France (EUR)\",\n        \"Australia (AUD)\",\n        \"Canada (CAD)\",\n        \"Italy (EUR)\",\n        \"Spain (EUR)\",\n        \"Netherlands (EUR)\",\n        \"Japan (JPY)\"\n      ],\n      \"default\": \"US\",\n      \"prefill\": \"US\"\n    }\n  },\n  \"required\": []\n}",
    "readme": "# Etsy Product Listings & Shop Research Scraper\n\nCollect Etsy listing data from Etsy's keyword market pages without a login, API key, or proxy setup. Give the actor a keyword or a market URL, choose one of ten regional markets, and get structured rows for pricing research, assortment tracking, and shop comparisons.\n\n## How it works\n\nEtsy's keyword market pages (`https://www.etsy.com/market/<slug>`) are the most reliable public route. The actor converts every search query into that route, reads the page's JSON-LD `ItemList` and listing cards, and follows pages until it reaches your limit or the source ends. When Etsy redirects a keyword to a category page, the actor follows the redirect with a fresh request instead of treating the redirect as a dead end.\n\nCategory URLs such as `https://www.etsy.com/c/jewelry` are also supported directly and normally expose about 64 cards per page. Legacy `/search?q=` URLs are converted to the equivalent market URL. Shop and individual listing URLs are accepted for backward compatibility, but Etsy protects those with DataDome; the actor reports them as blocked instead of returning an empty result.\n\n## What you get per listing\n\nCard-level fields:\n\n- `listingId` - numeric Etsy listing ID\n- `url` - listing page URL\n- `title` - listing title\n- `price`, `originalPrice` - plain decimal strings (no thousands separators)\n- `priceValue`, `originalPriceValue` - numeric values for calculations\n- `currency` - Etsy's display label or symbol (`USD`, `EUR`, `GBP`, `JPY`, `AU$`, `CA$`)\n- `currencyCode` - ISO 4217 code (`USD`, `EUR`, `GBP`, `JPY`, `AUD`, `CAD`, ...)\n- `discountPercent` - discount as a string, kept for compatibility\n- `discountPercentValue` - discount as an integer percentage\n- `imageUrl` - primary card image\n- `imageCount` - number of images exposed in the card (usually 1)\n- `shopId` - Etsy shop ID when the card exposes it\n- `shopName`, `shopUrl` - shop name and URL (from the page's brand data or shop link)\n- `rating`, `reviewCount` - shop-level rating and review count when the card shows them\n- `shopRating`, `shopReviewCount` - numeric shop-level copies of the same values\n- `ratingScope` - `shop` when the supplied rating is shop-level\n- `listingRating`, `listingReviewCount` - listing-level rating only when the page's structured data supplies `aggregateRating` (usually it does not)\n- `availability` - stock status from the page's structured data\n- `badges` - compatibility object with `starSeller`, `freeShipping`, `bestseller`, `etsyChoice`, `ad`\n- `isAd` - present and `true` for sponsored cards (omitted for organic cards)\n- `freeShipping`, `starSeller`, `bestseller`, `etsyChoice` - present and `true` when the badge is visible\n- `hasVariations` - present and `true` when variations are visible\n- `page` - source market or category page number\n- `position` - row position within the task's collected results\n- `sourceQuery` - keyword or slug that produced the row\n- `sourceUrl` - exact market or category page URL the row came from\n- `recordType` - `listing` for card records\n- `scrapedAt` - ISO 8601 collection timestamp\n\nFields the page does not expose are omitted rather than emitted as empty strings, nulls, or placeholders. `isAd`, badge flags, and `hasVariations` are only present when true; absence means the page did not show that signal. `rating` is a shop-level rating on market cards, which is why `ratingScope` is `shop`.\n\n### When `includeDetails` is enabled\n\nThe actor attempts to open each listing page and add:\n\n- `description`, `category`, `materials`, `listedDate`, `availability`\n- `images` - all listing image URLs\n- `variations` - selectable options\n- `tags` - listing tags\n- `sellerName`, `sellerRating`, `sellerReviewCount`, `sellerLocation`\n- `listingRating`, `listingReviewCount` - listing-level rating when the listing page supplies it\n- `detailsStatus` - `enriched`, `blocked`, `not_found`, `partial`, or `no_url`\n\nEtsy frequently blocks listing pages with DataDome. When that happens, the row keeps its card data, `detailsStatus` is `blocked`, and no detail fields are invented. A circuit breaker stops detail attempts after three consecutive blocks so a site-wide block cannot stall the run.\n\n## Input\n\nAt least one of `startUrls` or `searchQueries` is required for a normal run. A truly empty input uses the documented `handmade jewelry` fallback for the scheduled test. Apify's validator cannot express \"one of\", so the actor enforces it.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `startUrls` | array | - | Etsy market URLs are reliable; `/c/` category URLs work and return more cards per page. Legacy `/search?q=` URLs are converted to market URLs. Shop URLs use the 10-item public RSS feed; listing URLs are reported as blocked. |\n| `searchQueries` | array | - | Keywords mapped to Etsy market pages, with automatic category follow when Etsy redirects. Each query is a separate task. |\n| `maxItems` | integer | `100` | Maximum rows per URL or query (1-1,000). Keyword markets cap at 25 pages; categories can go deeper. |\n| `includeDetails` | boolean | `false` | Attempt listing-page enrichment and report `detailsStatus` honestly. |\n| `sortBy` | select | any | `most_relevant`, `price_asc`, `price_desc`, `date_desc`, `highest_reviews`. |\n| `minPrice` | number | - | Lower price bound in the market currency. Etsy `min` is used server-side and a local safeguard follows. |\n| `maxPrice` | number | - | Upper price bound in the market currency. |\n| `freeShippingOnly` | boolean | `false` | Use Etsy's `free_shipping=1` market filter. |\n| `starSellerOnly` | boolean | `false` | Use Etsy's `is_star_seller=1` market filter. |\n| `country` | string | `US` | Market locale: `US`, `DE`, `GB`, `FR`, `AU`, `CA`, `IT`, `ES`, `NL`, `JP`. |\n\nIf no input is supplied, the actor uses `handmade jewelry` as a safe fallback so a scheduled test run still produces data. At least one of `startUrls` or `searchQueries` is normally provided.\n\n### Examples\n\nKeyword search in the default US market:\n\n```json\n{\n  \"searchQueries\": [\"handmade jewelry\"],\n  \"maxItems\": 50\n}\n```\n\nMulti-page collection in Germany:\n\n```json\n{\n  \"searchQueries\": [\"keramik tasse\"],\n  \"country\": \"DE\",\n  \"maxItems\": 120\n}\n```\n\nDirect market URL:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.etsy.com/uk/market/wedding_ring\" }],\n  \"maxItems\": 80\n}\n```\n\nCategory URL with 64 cards per page:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.etsy.com/c/jewelry/necklaces\" }],\n  \"maxItems\": 200\n}\n```\n\nDetails requested, with honest fallback:\n\n```json\n{\n  \"searchQueries\": [\"personalized necklace\"],\n  \"includeDetails\": true,\n  \"maxItems\": 20\n}\n```\n\n## Pagination, deduplication, and limits\n\n- Pages are followed until `maxItems` is reached, no new listing IDs appear for two consecutive pages, or Etsy's explicit `max_page_exceeded` marker is hit.\n- A short page is not treated as the end. Etsy market pages normally show about eight cards, and category pages about 64; the old \"fewer than 64 cards means stop\" shortcut would have stopped after page one.\n- Rows are deduplicated by listing ID across pages and across tasks; sponsored cards can repeat.\n- Etsy serves at most 25 market pages per query (roughly 200 listings). Larger `maxItems` values return what exists and set a partial status message instead of pretending the request was complete.\n- Category pages can be deeper, but DataDome may rate-limit very deep or rapid page walks. The actor retries, then reports a partial result with the reason.\n- Some keywords have no market page, return 404, or redirect to a category page. The actor tries alternate slug spellings, follows a working category redirect, and reports the specific reason for any task that still fails.\n\n## FAQ\n\n**Do I need an Etsy account, API key, or proxy?**\nNo. The actor reads public market pages and works without credentials or proxy configuration. It chooses its proxy automatically and only after a direct request is blocked.\n\n**Why do I sometimes get fewer rows than `maxItems`?**\nEtsy's market route exposes up to 25 pages per query. The actor reports the ceiling and returns the available rows. Shop RSS is limited to 10 recent items. Category pages can go deeper but may rate-limit.\n\n**Which filters and sorts work?**\n`sortBy`, `freeShippingOnly`, `starSellerOnly`, `minPrice` and `maxPrice` are supported. Sorting and the two badges are server-side; price bounds use Etsy's `min`/`max` parameters and a local safeguard. Views, favorites, carts and bought-in-last-24h are not present on market/category pages; listing pages expose some of them only through a browser, so they are not shipped.\n\n**What happens with shop and listing URLs?**\nShop URLs use Etsy's public shop RSS feed, which returns the 10 most recent listings with title, description, image and price. It is honest about being a recent-items feed and does not paginate. Listing detail pages remain DataDome-blocked for plain HTTP; browser/proxy enrichment is not used in production because it is slow and unreliable. Keyword market pages and `/c/` category pages are the reliable bulk routes.\n\n**Why is `ratingScope` set to `shop`?**\nThe rating shown on an Etsy listing card is the shop's aggregate rating. This was verified by finding the same shop's separate listings carrying an identical rating and review count. Listing-level ratings are only claimed through `listingRating` when structured data on the page supplies them, or through `includeDetails` when the listing page is readable.\n\n**Why are some shop names or ratings missing?**\nEtsy's card markup does not always expose a shop name or rating. The actor fills them from the page's JSON-LD brand and visible rating controls, and omits them when the page genuinely does not show them. Missing fields are never replaced with placeholders.\n\n**Are prices strings or numbers?**\nBoth are provided. `price` and `originalPrice` remain decimal strings for backward compatibility; `priceValue` and `originalPriceValue` are numeric for calculations.\n\n**Why do output listing/shop URLs return 403?** Etsy's DataDome protection blocks clean clients even for URLs the actor collected from public market/RSS pages. The URLs are valid for a browser; this is an Etsy anti-bot caveat, not a broken URL. Image URLs were verified HTTP 200.\n\n**Is this affiliated with Etsy?**\nNo. This is an independent third-party actor that reads publicly visible pages and is not affiliated with or endorsed by Etsy, Inc.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "etsy-product-listings-and-shop-research-scraper",
      "title": "Etsy Product Listings & Shop Research Scraper",
      "description": "Collect Etsy product listings from keyword market and category pages with numeric prices, ISO currency codes, shop details, ratings, badges, and optional listing detail across ten regional markets.",
      "version": "1.2",
      "buildTag": "latest",
      "input": {
        "title": "Etsy Scraper Input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Etsy URLs",
            "type": "array",
            "description": "Etsy market URLs such as https://www.etsy.com/market/handmade_jewelry are the reliable route. Category URLs such as https://www.etsy.com/c/jewelry also work and expose more cards per page. Legacy /search?q= URLs are converted to the matching market URL automatically. Shop and individual listing URLs are usually blocked by Etsy's DataDome protection; they are accepted for backward compatibility and reported as blocked instead of silently returning nothing.",
            "editor": "requestListSources"
          },
          "searchQueries": {
            "title": "Search Queries",
            "type": "array",
            "description": "Keywords to search on Etsy. Each query is mapped to Etsy's market page (https://www.etsy.com/market/<slug>), the route that works without a browser or login. If Etsy redirects a keyword to a category page, the actor follows that category route. Each query is scraped as a separate task.",
            "editor": "stringList",
            "prefill": [
              "handmade jewelry"
            ]
          },
          "maxItems": {
            "title": "Max Items",
            "type": "integer",
            "description": "Maximum number of listings to collect per URL or search query. A keyword market exposes at most 25 pages (about 200 listings); category URLs can expose more pages. Larger values return what Etsy exposes and report a partial result instead of pretending the request was complete.",
            "default": 100,
            "prefill": 1,
            "minimum": 1,
            "maximum": 1000,
            "editor": "number"
          },
          "includeDetails": {
            "title": "Include Listing Details",
            "type": "boolean",
            "description": "Attempts to open each listing page for description, materials, tags, variations, images, and seller details. Etsy often blocks listing pages with DataDome. When that happens, rows are still returned with detailsStatus set to 'blocked' and the unavailable detail fields omitted; they are never filled with placeholders.",
            "default": false,
            "prefill": false
          },
          "sortBy": {
            "title": "Sort results by",
            "type": "string",
            "description": "Server-side Etsy ordering for market and category pages.",
            "editor": "select",
            "enum": [
              "",
              "most_relevant",
              "price_asc",
              "price_desc",
              "date_desc",
              "highest_reviews"
            ],
            "enumTitles": [
              "Most relevant",
              "Price: low to high",
              "Price: high to low",
              "Newest",
              "Most reviewed"
            ],
            "default": "",
            "prefill": ""
          },
          "minPrice": {
            "title": "Minimum price",
            "type": "number",
            "description": "Drop listings below this price in the selected market currency. Etsy does not expose a reliable server-side price filter on market pages, so this is applied to fetched cards. 0 disables the lower bound.",
            "minimum": 0,
            "maximum": 1000000,
            "default": 0,
            "prefill": 0,
            "editor": "number"
          },
          "maxPrice": {
            "title": "Maximum price",
            "type": "number",
            "description": "Drop listings above this price in the selected market currency. 0 disables the upper bound.",
            "minimum": 0,
            "maximum": 1000000,
            "default": 0,
            "prefill": 0,
            "editor": "number"
          },
          "freeShippingOnly": {
            "title": "Free shipping only",
            "type": "boolean",
            "description": "Use Etsy's free-shipping market filter.",
            "default": false,
            "prefill": false
          },
          "starSellerOnly": {
            "title": "Star Seller only",
            "type": "boolean",
            "description": "Use Etsy's Star Seller market filter.",
            "default": false,
            "prefill": false
          },
          "country": {
            "title": "Country / Market",
            "type": "string",
            "description": "Target Etsy market. Sets the market URL locale and request language, so prices and ranking match the selected region (for example Germany returns EUR and Japan returns JPY).",
            "editor": "select",
            "enum": [
              "US",
              "DE",
              "GB",
              "FR",
              "AU",
              "CA",
              "IT",
              "ES",
              "NL",
              "JP"
            ],
            "enumTitles": [
              "United States (USD)",
              "Germany (EUR)",
              "United Kingdom (GBP)",
              "France (EUR)",
              "Australia (AUD)",
              "Canada (CAD)",
              "Italy (EUR)",
              "Spain (EUR)",
              "Netherlands (EUR)",
              "Japan (JPY)"
            ],
            "default": "US",
            "prefill": "US"
          }
        },
        "required": []
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "Etsy Product Listings & Shop Research Scraper Output",
        "description": "Dataset of Etsy listing records collected from keyword market and category pages, with ISO currency codes, numeric prices, shop-level ratings, badges, provenance fields, and optional listing detail.",
        "properties": {
          "listings": {
            "type": "string",
            "title": "Etsy Listings",
            "description": "Dataset containing all scraped Etsy product listings.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "dockerfile": "../Dockerfile",
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Overview",
              "transformation": {
                "fields": [
                  "title",
                  "priceValue",
                  "currencyCode",
                  "shopName",
                  "rating",
                  "reviewCount",
                  "isAd",
                  "url"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "title": {
                    "label": "Title",
                    "format": "text"
                  },
                  "priceValue": {
                    "label": "Price",
                    "format": "number"
                  },
                  "currencyCode": {
                    "label": "Currency",
                    "format": "text"
                  },
                  "shopName": {
                    "label": "Shop",
                    "format": "text"
                  },
                  "rating": {
                    "label": "Shop rating",
                    "format": "text"
                  },
                  "reviewCount": {
                    "label": "Shop reviews",
                    "format": "text"
                  },
                  "isAd": {
                    "label": "Ad",
                    "format": "boolean"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  }
                }
              }
            }
          }
        }
      },
      "readme": "# Etsy Product Listings & Shop Research Scraper\n\nCollect Etsy listing data from Etsy's keyword market pages without a login, API key, or proxy setup. Give the actor a keyword or a market URL, choose one of ten regional markets, and get structured rows for pricing research, assortment tracking, and shop comparisons.\n\n## How it works\n\nEtsy's keyword market pages (`https://www.etsy.com/market/<slug>`) are the most reliable public route. The actor converts every search query into that route, reads the page's JSON-LD `ItemList` and listing cards, and follows pages until it reaches your limit or the source ends. When Etsy redirects a keyword to a category page, the actor follows the redirect with a fresh request instead of treating the redirect as a dead end.\n\nCategory URLs such as `https://www.etsy.com/c/jewelry` are also supported directly and normally expose about 64 cards per page. Legacy `/search?q=` URLs are converted to the equivalent market URL. Shop and individual listing URLs are accepted for backward compatibility, but Etsy protects those with DataDome; the actor reports them as blocked instead of returning an empty result.\n\n## What you get per listing\n\nCard-level fields:\n\n- `listingId` - numeric Etsy listing ID\n- `url` - listing page URL\n- `title` - listing title\n- `price`, `originalPrice` - plain decimal strings (no thousands separators)\n- `priceValue`, `originalPriceValue` - numeric values for calculations\n- `currency` - Etsy's display label or symbol (`USD`, `EUR`, `GBP`, `JPY`, `AU$`, `CA$`)\n- `currencyCode` - ISO 4217 code (`USD`, `EUR`, `GBP`, `JPY`, `AUD`, `CAD`, ...)\n- `discountPercent` - discount as a string, kept for compatibility\n- `discountPercentValue` - discount as an integer percentage\n- `imageUrl` - primary card image\n- `imageCount` - number of images exposed in the card (usually 1)\n- `shopId` - Etsy shop ID when the card exposes it\n- `shopName`, `shopUrl` - shop name and URL (from the page's brand data or shop link)\n- `rating`, `reviewCount` - shop-level rating and review count when the card shows them\n- `shopRating`, `shopReviewCount` - numeric shop-level copies of the same values\n- `ratingScope` - `shop` when the supplied rating is shop-level\n- `listingRating`, `listingReviewCount` - listing-level rating only when the page's structured data supplies `aggregateRating` (usually it does not)\n- `availability` - stock status from the page's structured data\n- `badges` - compatibility object with `starSeller`, `freeShipping`, `bestseller`, `etsyChoice`, `ad`\n- `isAd` - present and `true` for sponsored cards (omitted for organic cards)\n- `freeShipping`, `starSeller`, `bestseller`, `etsyChoice` - present and `true` when the badge is visible\n- `hasVariations` - present and `true` when variations are visible\n- `page` - source market or category page number\n- `position` - row position within the task's collected results\n- `sourceQuery` - keyword or slug that produced the row\n- `sourceUrl` - exact market or category page URL the row came from\n- `recordType` - `listing` for card records\n- `scrapedAt` - ISO 8601 collection timestamp\n\nFields the page does not expose are omitted rather than emitted as empty strings, nulls, or placeholders. `isAd`, badge flags, and `hasVariations` are only present when true; absence means the page did not show that signal. `rating` is a shop-level rating on market cards, which is why `ratingScope` is `shop`.\n\n### When `includeDetails` is enabled\n\nThe actor attempts to open each listing page and add:\n\n- `description`, `category`, `materials`, `listedDate`, `availability`\n- `images` - all listing image URLs\n- `variations` - selectable options\n- `tags` - listing tags\n- `sellerName`, `sellerRating`, `sellerReviewCount`, `sellerLocation`\n- `listingRating`, `listingReviewCount` - listing-level rating when the listing page supplies it\n- `detailsStatus` - `enriched`, `blocked`, `not_found`, `partial`, or `no_url`\n\nEtsy frequently blocks listing pages with DataDome. When that happens, the row keeps its card data, `detailsStatus` is `blocked`, and no detail fields are invented. A circuit breaker stops detail attempts after three consecutive blocks so a site-wide block cannot stall the run.\n\n## Input\n\nAt least one of `startUrls` or `searchQueries` is required for a normal run. A truly empty input uses the documented `handmade jewelry` fallback for the scheduled test. Apify's validator cannot express \"one of\", so the actor enforces it.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `startUrls` | array | - | Etsy market URLs are reliable; `/c/` category URLs work and return more cards per page. Legacy `/search?q=` URLs are converted to market URLs. Shop URLs use the 10-item public RSS feed; listing URLs are reported as blocked. |\n| `searchQueries` | array | - | Keywords mapped to Etsy market pages, with automatic category follow when Etsy redirects. Each query is a separate task. |\n| `maxItems` | integer | `100` | Maximum rows per URL or query (1-1,000). Keyword markets cap at 25 pages; categories can go deeper. |\n| `includeDetails` | boolean | `false` | Attempt listing-page enrichment and report `detailsStatus` honestly. |\n| `sortBy` | select | any | `most_relevant`, `price_asc`, `price_desc`, `date_desc`, `highest_reviews`. |\n| `minPrice` | number | - | Lower price bound in the market currency. Etsy `min` is used server-side and a local safeguard follows. |\n| `maxPrice` | number | - | Upper price bound in the market currency. |\n| `freeShippingOnly` | boolean | `false` | Use Etsy's `free_shipping=1` market filter. |\n| `starSellerOnly` | boolean | `false` | Use Etsy's `is_star_seller=1` market filter. |\n| `country` | string | `US` | Market locale: `US`, `DE`, `GB`, `FR`, `AU`, `CA`, `IT`, `ES`, `NL`, `JP`. |\n\nIf no input is supplied, the actor uses `handmade jewelry` as a safe fallback so a scheduled test run still produces data. At least one of `startUrls` or `searchQueries` is normally provided.\n\n### Examples\n\nKeyword search in the default US market:\n\n```json\n{\n  \"searchQueries\": [\"handmade jewelry\"],\n  \"maxItems\": 50\n}\n```\n\nMulti-page collection in Germany:\n\n```json\n{\n  \"searchQueries\": [\"keramik tasse\"],\n  \"country\": \"DE\",\n  \"maxItems\": 120\n}\n```\n\nDirect market URL:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.etsy.com/uk/market/wedding_ring\" }],\n  \"maxItems\": 80\n}\n```\n\nCategory URL with 64 cards per page:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.etsy.com/c/jewelry/necklaces\" }],\n  \"maxItems\": 200\n}\n```\n\nDetails requested, with honest fallback:\n\n```json\n{\n  \"searchQueries\": [\"personalized necklace\"],\n  \"includeDetails\": true,\n  \"maxItems\": 20\n}\n```\n\n## Pagination, deduplication, and limits\n\n- Pages are followed until `maxItems` is reached, no new listing IDs appear for two consecutive pages, or Etsy's explicit `max_page_exceeded` marker is hit.\n- A short page is not treated as the end. Etsy market pages normally show about eight cards, and category pages about 64; the old \"fewer than 64 cards means stop\" shortcut would have stopped after page one.\n- Rows are deduplicated by listing ID across pages and across tasks; sponsored cards can repeat.\n- Etsy serves at most 25 market pages per query (roughly 200 listings). Larger `maxItems` values return what exists and set a partial status message instead of pretending the request was complete.\n- Category pages can be deeper, but DataDome may rate-limit very deep or rapid page walks. The actor retries, then reports a partial result with the reason.\n- Some keywords have no market page, return 404, or redirect to a category page. The actor tries alternate slug spellings, follows a working category redirect, and reports the specific reason for any task that still fails.\n\n## FAQ\n\n**Do I need an Etsy account, API key, or proxy?**\nNo. The actor reads public market pages and works without credentials or proxy configuration. It chooses its proxy automatically and only after a direct request is blocked.\n\n**Why do I sometimes get fewer rows than `maxItems`?**\nEtsy's market route exposes up to 25 pages per query. The actor reports the ceiling and returns the available rows. Shop RSS is limited to 10 recent items. Category pages can go deeper but may rate-limit.\n\n**Which filters and sorts work?**\n`sortBy`, `freeShippingOnly`, `starSellerOnly`, `minPrice` and `maxPrice` are supported. Sorting and the two badges are server-side; price bounds use Etsy's `min`/`max` parameters and a local safeguard. Views, favorites, carts and bought-in-last-24h are not present on market/category pages; listing pages expose some of them only through a browser, so they are not shipped.\n\n**What happens with shop and listing URLs?**\nShop URLs use Etsy's public shop RSS feed, which returns the 10 most recent listings with title, description, image and price. It is honest about being a recent-items feed and does not paginate. Listing detail pages remain DataDome-blocked for plain HTTP; browser/proxy enrichment is not used in production because it is slow and unreliable. Keyword market pages and `/c/` category pages are the reliable bulk routes.\n\n**Why is `ratingScope` set to `shop`?**\nThe rating shown on an Etsy listing card is the shop's aggregate rating. This was verified by finding the same shop's separate listings carrying an identical rating and review count. Listing-level ratings are only claimed through `listingRating` when structured data on the page supplies them, or through `includeDetails` when the listing page is readable.\n\n**Why are some shop names or ratings missing?**\nEtsy's card markup does not always expose a shop name or rating. The actor fills them from the page's JSON-LD brand and visible rating controls, and omits them when the page genuinely does not show them. Missing fields are never replaced with placeholders.\n\n**Are prices strings or numbers?**\nBoth are provided. `price` and `originalPrice` remain decimal strings for backward compatibility; `priceValue` and `originalPriceValue` are numeric for calculations.\n\n**Why do output listing/shop URLs return 403?** Etsy's DataDome protection blocks clean clients even for URLs the actor collected from public market/RSS pages. The URLs are valid for a browser; this is an Etsy anti-bot caveat, not a broken URL. Image URLs were verified HTTP 200.\n\n**Is this affiliated with Etsy?**\nNo. This is an independent third-party actor that reads publicly visible pages and is not affiliated with or endorsed by Etsy, Inc.\n"
    },
    "buildNumber": "1.2.1",
    "imageDigest": "fa9288680333ba40567e3bdb3ce3d9be64f36965420bd4c3678669d43653e4b3",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.2"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.009347777777777778
    }
  }
}