{
  "schemaVersion": 1,
  "generatedAt": "2026-11-03T16:12:35.491Z",
  "actors": [
    {
      "slug": "glassdoor-reviews",
      "family": "reviews",
      "title": "Glassdoor Reviews Scraper: Sub-Ratings, Pros, Cons",
      "description": "Give it Glassdoor company URLs and it returns one JSON row per employee review: overall rating, six sub-ratings, pros, cons, advice to management, job title, employment status and the employer's reply.",
      "inputSchema": {
        "title": "Glassdoor Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Company review URLs",
            "type": "array",
            "description": "Glassdoor company review pages, for example https://www.glassdoor.com/Reviews/Google-Reviews-E9079.htm. The employer id is read from the URL.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.glassdoor.com/Reviews/Google-Reviews-E9079.htm"
              }
            ]
          },
          "employerIds": {
            "title": "Employer IDs",
            "type": "array",
            "description": "Numeric Glassdoor employer IDs (the number after E in the review page URL), as an alternative to URLs. Example: 9079.",
            "editor": "json",
            "default": []
          },
          "maxReviewsPerCompany": {
            "title": "Max reviews per company",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each company.",
            "default": 100,
            "minimum": 1,
            "maximum": 100000,
            "prefill": 20
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Order of the reviews. Newest first is required for the date filter and for only-new-reviews mode.",
            "enum": [
              "newest",
              "oldest",
              "highestRating",
              "lowestRating",
              "relevance"
            ],
            "enumTitles": [
              "Newest first",
              "Oldest first",
              "Highest rating first",
              "Lowest rating first",
              "Most relevant"
            ],
            "editor": "select",
            "default": "newest"
          },
          "stars": {
            "title": "Overall rating",
            "type": "integer",
            "description": "Only reviews with exactly this overall star rating (1 to 5). Leave empty for all ratings.",
            "minimum": 1,
            "maximum": 5
          },
          "employmentStatuses": {
            "title": "Employment status",
            "type": "array",
            "description": "Only reviews from people with these kinds of employment. Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "REGULAR",
                "PART_TIME",
                "CONTRACT",
                "INTERN",
                "FREELANCE"
              ],
              "enumTitles": [
                "Full-time",
                "Part-time",
                "Contractor",
                "Intern",
                "Freelancer"
              ]
            },
            "default": []
          },
          "onlyCurrentEmployees": {
            "title": "Current employees only",
            "type": "boolean",
            "description": "Skip reviews written by former employees.",
            "default": false
          },
          "jobTitle": {
            "title": "Job title",
            "type": "string",
            "description": "Only reviews from people with this job title, for example Software Engineer.",
            "editor": "textfield"
          },
          "locationId": {
            "title": "Location ID",
            "type": "integer",
            "description": "Optional numeric Glassdoor location id, as used in the location filter of the site's review pages. Reviews carry no location in the output; this only narrows the selection.",
            "minimum": 1
          },
          "locationType": {
            "title": "Location type",
            "type": "string",
            "description": "Whether the location ID is a city or a country.",
            "enum": [
              "CITY",
              "COUNTRY"
            ],
            "enumTitles": [
              "City",
              "Country"
            ],
            "editor": "select",
            "default": "CITY"
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Languages of the reviews to collect.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "en",
                "de",
                "fr",
                "es",
                "it",
                "nl",
                "pt"
              ],
              "enumTitles": [
                "English",
                "German",
                "French",
                "Spanish",
                "Italian",
                "Dutch",
                "Portuguese"
              ]
            },
            "default": [
              "en"
            ]
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only reviews on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). Works with newest-first sorting.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Return only reviews not delivered by a previous run with the same state key.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the saved state used by only-new-reviews mode. Use different keys for independent monitors. Example: weekly-monitoring.",
            "editor": "textfield",
            "default": "default"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add a free row to the dataset for each company that could not be read.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "type": "string",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "type": "string",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "type": "string",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "type": "object",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "type": "null",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "type": "null",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "type": "string",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "platform": "glassdoor",
          "reviewId": "Glassdoor___106026350",
          "date": "2026-10-19",
          "title": "Review and my experience",
          "pros": "Good wlb pay rsu and free snacks",
          "cons": "Bad management hire n fire",
          "adviceToOthers": "None",
          "ratingOverall": 3,
          "ratings": {
            "workLifeBalance": 3,
            "cultureAndValues": 3,
            "diversityAndInclusion": 3,
            "seniorLeadership": 3,
            "careerOpportunities": 4,
            "compensationAndBenefits": 3
          },
          "reviewerRole": null,
          "reviewerTimeUsed": null,
          "language": "en",
          "extras": {
            "employmentStatus": "regular",
            "currentEmployee": true,
            "tenureYears": 4,
            "recommendToFriend": null,
            "ceoApproval": null
          },
          "entityId": "9079",
          "productName": "Google"
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.003,
            "title": "Review"
          },
          "only-new-check": {
            "usd": 0.035,
            "title": "Only-new check (100 reviews re-read)"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.024,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-03T08:26:42.000Z"
    },
    {
      "slug": "booking-reviews",
      "family": "reviews",
      "title": "Booking.com Reviews Scraper: Pros, Cons, Reviewer Country",
      "description": "Give it Booking.com hotel URLs and it returns one JSON row per guest review: score (1-10 and on a 0-5 scale), what the guest liked and disliked as separate fields, title, date, language, traveler type, room type, nights, reviewer country and the hotel's reply.",
      "inputSchema": {
        "title": "Booking Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Hotel URLs",
            "type": "array",
            "description": "Booking.com hotel pages or hotel review pages, for example https://www.booking.com/hotel/gb/the-savoy.html.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.booking.com/hotel/gb/the-savoy.html"
              }
            ]
          },
          "hotelIds": {
            "title": "Hotel IDs",
            "type": "array",
            "description": "Numeric Booking.com hotel IDs, as an alternative to URLs. Rows from IDs carry no hotel name.",
            "editor": "json",
            "default": []
          },
          "maxReviewsPerHotel": {
            "title": "Max reviews per hotel",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each hotel. Booking only shows about the last three years.",
            "default": 100,
            "minimum": 1,
            "maximum": 50000,
            "prefill": 20
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Order of the reviews. Newest first is required for the date filter and for only-new-reviews mode.",
            "enum": [
              "NEWEST_FIRST",
              "OLDEST_FIRST",
              "SCORE_DESC",
              "SCORE_ASC",
              "MOST_RELEVANT"
            ],
            "enumTitles": [
              "Newest first",
              "Oldest first",
              "Highest score first",
              "Lowest score first",
              "Most relevant"
            ],
            "editor": "select",
            "prefill": "NEWEST_FIRST"
          },
          "travelerTypes": {
            "title": "Traveler types",
            "type": "array",
            "description": "Only reviews from these kinds of travelers. Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "FAMILIES",
                "COUPLES",
                "GROUP_OF_FRIENDS",
                "SOLO_TRAVELLERS",
                "BUSINESS_TRAVELLERS"
              ],
              "enumTitles": [
                "Families",
                "Couples",
                "Groups of friends",
                "Solo travelers",
                "Business travelers"
              ]
            },
            "default": []
          },
          "scoreRanges": {
            "title": "Score bands",
            "type": "array",
            "description": "Only reviews in these score bands: Superb 9+, Good 7-9, Passable 5-7, Poor 3-5, Very poor 1-3.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "SUPERB",
                "GOOD",
                "PASSABLE",
                "POOR",
                "VERY_POOR"
              ],
              "enumTitles": [
                "Superb (9+)",
                "Good (7-9)",
                "Passable (5-7)",
                "Poor (3-5)",
                "Very poor (1-3)"
              ]
            }
          },
          "seasons": {
            "title": "Stay seasons",
            "type": "array",
            "description": "Only reviews of stays in these periods of the year.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "MAR_MAY",
                "JUN_AUG",
                "SEP_NOV",
                "DEC_FEB"
              ],
              "enumTitles": [
                "March to May",
                "June to August",
                "September to November",
                "December to February"
              ]
            },
            "default": []
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Two-letter language codes such as en or de. Leave empty for all languages.",
            "editor": "stringList",
            "default": []
          },
          "topics": {
            "title": "Topic IDs",
            "type": "array",
            "description": "Optional numeric topic filters as used by the site, for example 245 for breakfast, 249 for location, 270 for room.",
            "editor": "json",
            "default": []
          },
          "keyword": {
            "title": "Keyword",
            "type": "string",
            "description": "Only reviews that mention this word or phrase. Example: breakfast.",
            "editor": "textfield"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only reviews on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). Works with newest-first sorting.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Return only reviews not delivered by a previous run with the same state key.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the saved state used by only-new-reviews mode. Use different keys for independent monitors. Example: weekly-monitoring.",
            "editor": "textfield",
            "default": "default"
          },
          "includeHotelSummary": {
            "title": "Include hotel summary row",
            "type": "boolean",
            "description": "Add one free row per hotel with the overall score and category scores (cleanliness, location, staff and more) compared with the city average.",
            "default": false
          },
          "includeReviewerCountry": {
            "title": "Include reviewer country",
            "type": "boolean",
            "description": "Add the reviewer's country as a two-letter ISO code in the country field. On by default; no names or photos are ever returned.",
            "default": true
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add a free row to the dataset for each hotel that could not be read.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'hotel-summary': one row per hotel with its overall ratings (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "type": "string",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Always null: Booking.com gives a single review no link of its own. Use reviewId to deduplicate."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "type": "string",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "type": "null",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "type": "string",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "platform": "booking",
          "reviewId": "Booking___c6397d1e2a854aa3",
          "date": "2026-09-30",
          "title": "Perfect stay",
          "pros": "Staff went out of their way for us.",
          "cons": null,
          "ratingOverall": 5,
          "language": "en",
          "vendorResponse": "Dear [name],\nThank you for staying with us...",
          "extras": {
            "score10": 10,
            "travelerType": "COUPLES",
            "roomType": "Superior King",
            "nights": 3
          },
          "entityId": "280149",
          "productName": "The Savoy"
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.001,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:17:03.000Z"
    },
    {
      "slug": "tripadvisor-reviews",
      "family": "reviews",
      "title": "Tripadvisor Reviews Scraper: Ratings, Replies, Translations",
      "description": "Give it Tripadvisor hotel, restaurant or attraction pages (or place names) and it returns one JSON row per review: rating, title, original-language text with an optional translation, date, sub-ratings, trip type, the owner reply with its date, and the place's overall rating and review count.",
      "inputSchema": {
        "title": "Tripadvisor Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Tripadvisor place URLs",
            "type": "array",
            "description": "Hotel, restaurant or attraction pages, e.g. https://www.tripadvisor.com/Hotel_Review-g60763-d93450-Reviews-Hyatt_Grand_Central_New_York-New_York_City_New_York.html. Any Tripadvisor domain works. Add this, search queries, or both.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.tripadvisor.com/Hotel_Review-g60763-d93450-Reviews-Hyatt_Grand_Central_New_York-New_York_City_New_York.html"
              }
            ]
          },
          "searchQueries": {
            "title": "Search by name",
            "type": "array",
            "description": "Place names to look up, for example 'hyatt grand central new york'. The best match for each name is scraped. Use URLs when you need an exact place.",
            "editor": "stringList"
          },
          "maxReviewsPerPlace": {
            "title": "Max reviews per place",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each place (up to 50,000). Default: 100 when omitted.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 20
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Order of the reviews. Most recent is newest first and enables the date filter and the incremental mode.",
            "enum": [
              "MOST_RECENT",
              "DETAILED",
              "INSIGHTFUL"
            ],
            "enumTitles": [
              "Most recent",
              "Most detailed",
              "Most insightful"
            ],
            "default": "MOST_RECENT",
            "editor": "select"
          },
          "ratings": {
            "title": "Ratings",
            "type": "array",
            "description": "Only reviews with these star ratings (1 to 5). Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "1",
                "2",
                "3",
                "4",
                "5"
              ],
              "enumTitles": [
                "1 star",
                "2 stars",
                "3 stars",
                "4 stars",
                "5 stars"
              ]
            }
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Only reviews written in these languages, as two-letter codes such as en, de, fr. Leave empty for all languages. Each language is read in its own pass.",
            "editor": "stringList"
          },
          "tripTypes": {
            "title": "Trip types",
            "type": "array",
            "description": "Only reviews from these kinds of travelers. Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "BUSINESS",
                "COUPLES",
                "FAMILY",
                "FRIENDS",
                "SOLO"
              ],
              "enumTitles": [
                "Business",
                "Couples",
                "Family",
                "Friends",
                "Solo"
              ]
            }
          },
          "visitMonths": {
            "title": "Months of visit",
            "type": "array",
            "description": "Only reviews about visits in these months (any year). Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "JANUARY",
                "FEBRUARY",
                "MARCH",
                "APRIL",
                "MAY",
                "JUNE",
                "JULY",
                "AUGUST",
                "SEPTEMBER",
                "OCTOBER",
                "NOVEMBER",
                "DECEMBER"
              ],
              "enumTitles": [
                "January",
                "February",
                "March",
                "April",
                "May",
                "June",
                "July",
                "August",
                "September",
                "October",
                "November",
                "December"
              ]
            }
          },
          "keyword": {
            "title": "Keyword",
            "type": "string",
            "description": "Only reviews that mention this word or phrase. Tripadvisor returns at most 1,000 matches for a keyword search. Example: breakfast.",
            "editor": "textfield"
          },
          "translateTo": {
            "title": "Translate to",
            "type": "string",
            "description": "Language code (for example en). Reviews written in other languages get a machine translation in extras.translatedText next to the original text. Tripadvisor only translates reviews from roughly the last two years. Leave empty for originals only.",
            "editor": "textfield"
          },
          "includePlaceSummary": {
            "title": "Add place rating and review count",
            "type": "boolean",
            "description": "Adds the place's overall rating and total review count to every row (product.rating, product.reviewCount). Costs five small extra requests per place; turn off if you only need the review text.",
            "default": true
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only keep reviews written on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). With the most recent sort, reading stops once reviews are older.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Skip reviews delivered by earlier runs that used the same state key, and do not charge for them. Forces the most recent sort.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the incremental state (letters, digits and dashes). Change it to start over. Example: weekly-monitoring.",
            "default": "default",
            "editor": "textfield"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add one row per place that could not be read, with the reason. These rows are free.",
            "default": true
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "type": "object",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "type": "string",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "reviewId": "Tripadvisor___123456789",
          "platform": "tripadvisor",
          "productName": "Example Grand Hotel",
          "date": "2026-09-27",
          "title": "Great stay",
          "text": "Quiet room, friendly staff.",
          "ratingOverall": 5,
          "ratings": {
            "value": 5,
            "cleanliness": 5
          },
          "language": "en",
          "vendorResponse": "Dear [name], thank you for staying with us.",
          "extras": {
            "tripType": "COUPLES",
            "visitDate": "2026-08",
            "placeType": "ACCOMMODATION"
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.00075,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:55:05.000Z"
    },
    {
      "slug": "google-hotel-reviews",
      "family": "reviews",
      "title": "Google Hotel Reviews Scraper: Topics, Replies, Partner Sites",
      "description": "Give it Google Hotels links or hotel names and it returns every guest review Google shows as one JSON row each: rating, original text plus Google's translation, date, trip type and party, the rooms/service/location scores, the hotel's reply, and the partner-site reviews Google lists next to its own (Tripadvisor, Trip.com and others). Each hotel also gets a free summary row with its star histogram and topic breakdown.",
      "inputSchema": {
        "title": "Google Hotel Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "hotels": {
            "title": "Hotels (links, tokens or names)",
            "type": "array",
            "sectionCaption": "Which hotels",
            "description": "Hotels to read: a Google Hotels or Google Travel link such as https://www.google.com/travel/hotels/entity/ChUImcGKtLfWgL9OGgkvbS8wNTZuODEQAQ, the entity token alone (the Ch... part, also the entityId of every row), a hotel name with its city such as \"Hotel Adlon Kempinski Berlin\", or a Google Maps place link (read by its name). Each hotel returns its reviews plus one free summary row.",
            "editor": "stringList",
            "prefill": [
              "https://www.google.com/travel/hotels/entity/ChUImcGKtLfWgL9OGgkvbS8wNTZuODEQAQ",
              "Hotel Adlon Kempinski Berlin"
            ]
          },
          "maxReviewsPerHotel": {
            "title": "Max reviews per hotel",
            "type": "integer",
            "description": "Reviews to return for each hotel, 1 to 50,000 (default 100 when omitted), for example 500. Google pages up to 200 reviews per request; big hotels have 5,000+.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 20
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "sectionCaption": "Order and filters",
            "description": "Order of the reviews as Google sorts them. Newest first is needed for the date filter to stop early and for only-new-reviews mode.",
            "enum": [
              "newest",
              "most_helpful",
              "highest",
              "lowest"
            ],
            "enumTitles": [
              "Newest first",
              "Most helpful",
              "Highest score first",
              "Lowest score first"
            ],
            "editor": "select",
            "prefill": "newest"
          },
          "reviewSource": {
            "title": "Review source",
            "type": "string",
            "description": "Google's own reviews, the reviews of partner sites Google shows for the hotel (Tripadvisor, Trip.com, Expedia, ...), or both. Partner sites appear with a sample of their reviews and a link to each.",
            "enum": [
              "all",
              "google",
              "partners"
            ],
            "enumTitles": [
              "All sources",
              "Google reviews only",
              "Partner sites only"
            ],
            "editor": "select",
            "prefill": "all"
          },
          "topic": {
            "title": "Topic",
            "type": "string",
            "description": "Only reviews about one topic Google lists for the hotel, for example \"Location\", \"Breakfast\", \"Couples\" or \"Service\". A hotel without that topic returns a free error row that lists its topics.",
            "editor": "textfield"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only reviews on or after this date: YYYY-MM-DD or a relative span such as \"30 days\" or \"6 months\"; empty = no filter. Google shows review dates as \"3 months ago\", so dates are approximate to the day, week, month or year (see extras.datePrecision).",
            "editor": "textfield"
          },
          "language": {
            "title": "Language",
            "type": "string",
            "sectionCaption": "Language and market",
            "description": "Interface language as a code such as en, de, fr or pt-BR. Reviews in other languages come with Google's translation into this language in extras.textTranslated; the text field keeps the original. Dates are read for en, de, fr, es, it, pt and nl.",
            "editor": "textfield",
            "prefill": "en"
          },
          "country": {
            "title": "Country",
            "type": "string",
            "description": "Market as a two-letter country code, for example us, gb or de. It changes which partner sites Google lists.",
            "editor": "textfield",
            "prefill": "us"
          },
          "includeHotelSummary": {
            "title": "Include hotel summary row",
            "type": "boolean",
            "sectionCaption": "Output",
            "description": "Add one free row per hotel (rowType 'hotel-summary'): Google rating, review count, star histogram, the topic breakdown (mentions, positive, negative, sentiment) and the partner sites with their own rating and review count.",
            "default": true,
            "editor": "checkbox"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "sectionCaption": "Monitoring",
            "description": "Return only reviews not delivered by a previous run with the same state key (newest first is used automatically).",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the saved state used by only-new-reviews mode. Use different keys for independent monitors. Example: weekly-monitoring.",
            "editor": "textfield",
            "default": "default"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text or rating changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add a free row (rowType 'error') for each hotel that could not be read, with a machine-readable code.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per hotel: the review total Google states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'hotel-summary': one row per hotel with its Google rating, star histogram, topic breakdown and partner sites (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always 'googlehotels' (Google Hotels / Google Travel). The site a review was first posted on is in extras.source."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the hotel as Google shows it."
        },
        {
          "name": "productUrl",
          "description": "Google Hotels page of the hotel (https://www.google.com/travel/hotels/entity/<token>)."
        },
        {
          "name": "entityId",
          "type": "string",
          "description": "Google's hotel entity token (Ch...). Pass it back as a hotel input to read the same hotel again."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id 'GoogleHotels___<Google post id>'. The same in every language; use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the review on the partner site (Tripadvisor, Trip.com, ...) for partner reviews; null for Google reviews."
        },
        {
          "name": "title",
          "description": "Always null: Google hotel reviews have no headline."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text as the reviewer wrote it (original language). Google translation in extras.textTranslated. Null for rating-only reviews."
        },
        {
          "name": "pros",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "cons",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 1 to 5 scale (partner ratings are scaled to 5; the raw value is in extras.rawRating and extras.rawScale)."
        },
        {
          "name": "ratings",
          "type": "object",
          "description": "Aspect ratings the reviewer gave, 1 to 5, e.g. { rooms: 4, service: 2, location: 5 }. Empty object when none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Approximate review date yyyy-mm-dd, computed from Google's relative date ('3 months ago'); precision in extras.datePrecision. Null if unreadable."
        },
        {
          "name": "reviewerRole",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reviewerIndustry",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "switchedFrom",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "alternativesConsidered",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reasonForChoosing",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reasonForSwitching",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "adviceToOthers",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "incentivized",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "incentive",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "reviewerVerified",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "vendorResponse",
          "type": "string",
          "description": "Reply of the hotel (owner response) in its original language, names redacted. Translation in extras.ownerResponseTranslated, date in extras.ownerResponseDate."
        },
        {
          "name": "helpfulVotes",
          "description": "Always null on Google hotel reviews (shared review shape)."
        },
        {
          "name": "language",
          "type": "null",
          "description": "Always null: Google does not state the review language. A translated review has extras.translatedTo."
        },
        {
          "name": "country",
          "description": "Always null: Google shows no reviewer country. No personal data."
        },
        {
          "name": "product",
          "description": "Hotel summary repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the Google figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Google hotel extras: source (Google, Tripadvisor, ...), sourceType (google/partner), dateText, datePrecision, edited, textTranslated, translatedTo, tripType (vacation/business), travellerType (solo/couple/family/friends), visitMonth, highlights, topicComments, ownerResponseTranslated, ownerResponseDate, ownerResponseDateText, photoCount, rawRating, rawScale."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        },
        {
          "name": "ratingHistogram",
          "description": "Hotel summary rows: share in percent and count per star, e.g. { \"5\": { \"percent\": 86, \"count\": 3931 } }."
        },
        {
          "name": "topics",
          "description": "Hotel summary rows: Google topic breakdown, one entry per topic: { name, shortName, mentions, positive, negative, sentiment (-1 to 1), polarity }."
        },
        {
          "name": "partnerSites",
          "description": "Hotel summary rows: partner review sites Google lists, { name, providerId, rating, ratingScale, reviewCount, url }."
        },
        {
          "name": "rating",
          "description": "Hotel summary rows: Google rating of the hotel, 1 to 5."
        },
        {
          "name": "reviewCount",
          "description": "Hotel summary rows: number of Google reviews of the hotel."
        },
        {
          "name": "hotelClass",
          "description": "Hotel summary rows: star class (1-5), null when Google gives none."
        },
        {
          "name": "address",
          "description": "Hotel summary rows: street address of the hotel as Google shows it."
        },
        {
          "name": "countryCode",
          "description": "Hotel summary rows: country code of the hotel, null if unknown."
        }
      ],
      "sampleRows": [
        {
          "rowType": "review",
          "platform": "googlehotels",
          "productName": "Ritz Paris",
          "entityId": "ChUImcGKtLfWgL9OGgkvbS8wNTZuODEQAQ",
          "reviewId": "GoogleHotels___Ci9DQUlRQUNvZENodHljRjlvT25GSlZXbzJkV1Z0VTNwdmVGTXRVblUxY2s5aE1WRRAB",
          "ratingOverall": 4,
          "ratings": {
            "rooms": 3,
            "service": 4,
            "location": 3
          },
          "date": "2026-10-16",
          "text": "이곳은 아름다운스파 수영장 레스토랑 노을을 볼수있는곳 까지있어서 너무아름다워요 고급프랑스느낌",
          "vendorResponse": "Cher [name],\n\nThank you for taking the time to share your wonderful feedback. ...\n\nKind regards,\n\n[name]\n Directrice d'hôtel, Ritz Paris",
          "language": null,
          "extras": {
            "source": "Google",
            "sourceType": "google",
            "dateText": "6 days ago",
            "datePrecision": "day",
            "textTranslated": "This place is so beautiful, with a lovely spa, swimming pool, restaurant, and even a spot to watch the sunset. It has a luxurious French feel.",
            "translatedTo": "en",
            "tripType": "vacation",
            "travellerType": "couple",
            "visitMonth": "2026-10",
            "highlights": [
              "Luxury",
              "Romantic"
            ],
            "ownerResponseDate": "2026-10-17",
            "photoCount": null
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.0005,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:55:05.000Z"
    },
    {
      "slug": "agoda-tripcom-reviews",
      "family": "reviews",
      "title": "Agoda and Trip.com Reviews Scraper: Hotel Reviews, Replies",
      "description": "Give it Agoda or Trip.com hotel links (or hotel IDs) and it returns one JSON row per guest review: score, the text in the language it was written in plus the site's English translation when it has one, traveler type, stay details and the hotel's reply.",
      "inputSchema": {
        "title": "Agoda and Trip.com Hotel Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Hotel links",
            "type": "array",
            "description": "Agoda or Trip.com hotel pages, mixed freely, for example https://www.agoda.com/ibis-bangkok-riverside/hotel/bangkok-th.html or https://www.trip.com/hotels/detail/?hotelId=425587. Give a link here or an ID below. A single `hotelUrl` (other Agoda/Trip.com actors) is accepted too.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.agoda.com/ibis-bangkok-riverside/hotel/bangkok-th.html"
              },
              {
                "url": "https://www.trip.com/hotels/detail/?hotelId=425587"
              }
            ]
          },
          "agodaHotelIds": {
            "title": "Agoda hotel IDs",
            "type": "array",
            "description": "Numeric Agoda hotel IDs as an alternative to links, for example 230691 (the number after hotel_id= or selectedproperty= in Agoda links).",
            "editor": "json",
            "default": []
          },
          "tripcomHotelIds": {
            "title": "Trip.com hotel IDs",
            "type": "array",
            "description": "Numeric Trip.com hotel IDs as an alternative to links, for example 425587 (the hotelId in a Trip.com hotel link).",
            "editor": "json",
            "default": []
          },
          "maxReviewsPerHotel": {
            "title": "Max reviews per hotel",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each hotel. Both sites can be read to the last review. Also accepted as `maxReviews`. Default 100.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 5
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Order of the reviews. Newest first is required for only-new-reviews mode and makes the date filter stop early. Other actors' `sortBy`/`reviewsSort` values (mostRecent, mostRelevant, ratingHighToLow, ratingLowToHigh) are accepted. Default NEWEST.",
            "enum": [
              "NEWEST",
              "MOST_RELEVANT",
              "RATING_DESC",
              "RATING_ASC"
            ],
            "enumTitles": [
              "Newest first",
              "Most relevant (Agoda: most helpful)",
              "Highest rating first",
              "Lowest rating first"
            ],
            "editor": "select",
            "prefill": "NEWEST"
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Only reviews written in these languages, as two-letter codes such as en, ja or zh. Leave empty for all languages. Also accepted as `language` or `reviewLanguages` (en-us style codes are cut to en).",
            "editor": "stringList"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only reviews on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). Also accepted as `cutoffDate` or `fromDate`.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Return only reviews not delivered by a previous run with the same state key.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the saved state used by only-new-reviews mode. Use different keys for independent monitors. Example: weekly-monitoring.",
            "editor": "textfield",
            "default": "default"
          },
          "includeHotelSummary": {
            "title": "Include hotel summary row",
            "type": "boolean",
            "description": "Add one free row per hotel with the overall score, category scores, review counts and (Trip.com) the advertised from-price. Default off."
          },
          "includeReviewerCountry": {
            "title": "Include reviewer country",
            "type": "boolean",
            "description": "Add the reviewer's country as a two-letter ISO code in the country field. On by default; no names or photos are ever returned.",
            "default": true
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add a free row to the dataset for each hotel that could not be read.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per hotel: the review total the site states, how many were delivered and why paging ended. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'hotel-summary': one row per hotel with its overall ratings (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site: 'agoda' or 'tripcom'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "type": "string",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (the site's 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "type": "string",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "type": "string",
          "description": "Reviewer country as a two-letter ISO code (upper case) when the site shows it and includeReviewerCountry is on, otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras: score10, travelerType, roomType, stay dates, textTranslated (the site's English translation when it has one, else null), ownerResponseDate, photoCount. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "platform": "agoda",
          "reviewId": "Agoda___1190000001",
          "date": "2026-10-08",
          "title": "また泊まりたいホテル",
          "text": "スタッフの対応がとても良く、部屋も清潔でした。川沿いの景色も最高です。",
          "ratingOverall": 5,
          "language": "ja",
          "country": "TH",
          "vendorResponse": "Dear Guest, thank you for staying with us...",
          "extras": {
            "score10": 10,
            "textTranslated": "The staff were very helpful and the room was clean. The river view is great too.",
            "travelerType": "Couple",
            "roomType": "Superior Room",
            "nights": 3,
            "checkIn": "2026-10-05"
          },
          "entityId": "230691",
          "productName": "Ibis Bangkok Riverside"
        }
      ],
      "prices": {
        "events": {
          "agoda-review": {
            "usd": 0.001,
            "title": "Agoda review"
          },
          "tripcom-review": {
            "usd": 0.001,
            "title": "Trip.com review"
          }
        },
        "primaryEvent": "agoda-review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:14:10.000Z"
    },
    {
      "slug": "yelp-reviews",
      "family": "reviews",
      "title": "Yelp Reviews Scraper: All Languages, Owner Replies",
      "description": "Monitor what customers say about your locations or a competitor's, check whether owners answer complaints, or feed review text into sentiment analysis. Give it Yelp business pages (or business IDs) and it returns one JSON row per review: star rating, the full text in its original language, date, useful/funny/cool votes, reactions, the owner's public reply with its date, and the business's overall rating and review count.",
      "inputSchema": {
        "title": "Yelp Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Business URLs",
            "type": "array",
            "description": "Yelp business pages, for example https://www.yelp.com/biz/tartine-bakery-san-francisco-3. Any Yelp domain works. Add this, business IDs, or both. Also read from biz_urls or directUrls (other Yelp actors' names).",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.yelp.com/biz/tartine-bakery-san-francisco-3"
              }
            ]
          },
          "businessIds": {
            "title": "Business IDs or aliases",
            "type": "array",
            "description": "Yelp business IDs (22 characters, for example zLOmLckX_sNmhUwMW6yQMg) or aliases (the last part of the business URL). Use this instead of URLs, or next to them. Also read from biz_ids.",
            "editor": "stringList"
          },
          "maxReviewsPerBusiness": {
            "title": "Max reviews per business",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each business (default 100 when omitted). There is no depth limit: a business with 9,000 reviews can be read to the end. Example: 100. Also read from reviews_limit or maxReviewsPerUrl.",
            "minimum": 1,
            "maximum": 1000000,
            "prefill": 20
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Order of the reviews. Newest first enables the date filter and the only-new-reviews mode. With several languages, newest first merges them into one list ordered by date; other orders read one language after another.",
            "enum": [
              "DATE_DESC",
              "DATE_ASC",
              "RELEVANCE_DESC",
              "RATING_DESC",
              "RATING_ASC",
              "ELITES_DESC"
            ],
            "enumTitles": [
              "Newest first",
              "Oldest first",
              "Yelp sort (recommended)",
              "Highest rated first",
              "Lowest rated first",
              "Elite reviewers first"
            ],
            "editor": "select",
            "prefill": "DATE_DESC"
          },
          "ratings": {
            "title": "Star ratings",
            "type": "array",
            "description": "Only reviews with these star ratings (1 to 5). Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "1",
                "2",
                "3",
                "4",
                "5"
              ],
              "enumTitles": [
                "1 star",
                "2 stars",
                "3 stars",
                "4 stars",
                "5 stars"
              ]
            }
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Two-letter language codes such as en or de. Leave empty to read every language the business has reviews in, in one run. Also read from reviews_language or language (one code; empty = all).",
            "editor": "stringList"
          },
          "keywords": {
            "title": "Reviews must contain",
            "type": "array",
            "description": "Only reviews whose text contains at least one of these words or phrases (case-insensitive). The filter is applied after reading, and you only pay for the reviews that match. Example: [\"parking\", \"vegan\"].",
            "editor": "stringList"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only reviews on or after this date (YYYY-MM-DD, or 30 days / 2 months / 1 year). Reading stops early with the newest first sort. Example: 2026-01-01.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Skip reviews delivered by earlier runs that used the same state key, and do not charge for them. Forces the newest first sort.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the incremental state (letters, digits and dashes). Change it to start over. Example: weekly-monitoring.",
            "default": "default",
            "editor": "textfield"
          },
          "includeBusinessSummary": {
            "title": "Include business summary row",
            "type": "boolean",
            "description": "Add one free row per business with its overall rating, review count, star histogram and review count per language.",
            "default": false
          },
          "includeReviewPhotos": {
            "title": "Include review photo links",
            "type": "boolean",
            "description": "Add the links of photos attached to each review in extras.photoUrls. Off by default because photos can show people.",
            "default": false
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add one free row per business that could not be read, with the reason.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row per business: how many reviews Yelp reports, how many you received, and why reading stopped.",
            "default": true
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'business-summary': one row per business with its overall rating and review count (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "type": "string",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "type": "number",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "reviewId": "Yelp___abc123",
          "platform": "yelp",
          "productName": "Katz's Delicatessen",
          "date": "2026-10-09",
          "text": "Great pastrami, long line, worth it.",
          "ratingOverall": 4,
          "language": "en",
          "helpfulVotes": 2,
          "vendorResponse": "Hi [name], thank you for the kind words.",
          "extras": {
            "reactions": {
              "helpful": 2,
              "thanks": 0,
              "loveThis": 1,
              "ohNo": 0
            },
            "vendorResponseDate": "2026-10-10"
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.00025,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:55:05.000Z"
    },
    {
      "slug": "trustradius-reviews",
      "family": "reviews",
      "title": "TrustRadius Reviews Scraper: Pros, Cons, Reviewer Role",
      "description": "Give it TrustRadius product pages or product names and it returns one JSON row per review: rating, title, use case, pros, cons, the reviewer's job role and company size, and the incentive flag.",
      "inputSchema": {
        "title": "TrustRadius Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "TrustRadius product URLs",
            "type": "array",
            "description": "Product pages such as https://www.trustradius.com/products/asana/reviews (the /reviews part is optional). A link to a single review also works and resolves to its product. Add this, product names, or both.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.trustradius.com/products/asana/reviews"
              }
            ]
          },
          "products": {
            "title": "Product names",
            "type": "array",
            "description": "Product names to look up, for example Asana or Salesforce Sales Cloud. The closest match with public reviews is used. Names that do not match anything produce a free error row.",
            "editor": "stringList"
          },
          "maxReviewsPerProduct": {
            "title": "Max reviews per product",
            "type": "integer",
            "description": "Maximum number of reviews to collect for each product (up to 50,000). Default: 100.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 20
          },
          "sortBy": {
            "title": "Sort by",
            "type": "string",
            "description": "Sort reviews by date or by rating. Date with newest first enables the date filter and the incremental mode. Default: date.",
            "enum": [
              "date",
              "rating"
            ],
            "enumTitles": [
              "Date",
              "Rating"
            ],
            "editor": "select",
            "prefill": "date"
          },
          "sortOrder": {
            "title": "Sort order",
            "type": "string",
            "description": "Descending is newest or highest rated first. Default: desc.",
            "enum": [
              "desc",
              "asc"
            ],
            "enumTitles": [
              "Descending",
              "Ascending"
            ],
            "editor": "select",
            "prefill": "desc"
          },
          "starRating": {
            "title": "Rating groups",
            "type": "array",
            "description": "Only reviews in these rating groups. TrustRadius scores reviews from 1 to 10, grouped as 5 = scores 9-10, 4 = 7-8, 3 = 5-6, 2 = 3-4, 1 = 1-2. Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "5",
                "4",
                "3",
                "2",
                "1"
              ],
              "enumTitles": [
                "5 (scores 9-10)",
                "4 (scores 7-8)",
                "3 (scores 5-6)",
                "2 (scores 3-4)",
                "1 (scores 1-2)"
              ]
            }
          },
          "companySizeGroup": {
            "title": "Company size",
            "type": "string",
            "description": "Only reviews from reviewers at companies of this size. Leave empty for all sizes.",
            "enum": [
              "small",
              "medium",
              "large"
            ],
            "enumTitles": [
              "Small (1-50 employees)",
              "Medium (51-1,000 employees)",
              "Large (1,001+ employees)"
            ],
            "editor": "select"
          },
          "lastUpdated": {
            "title": "Reviews updated within",
            "type": "string",
            "description": "Only reviews added or updated within this period.",
            "enum": [
              "any",
              "month",
              "3months",
              "6months",
              "year"
            ],
            "enumTitles": [
              "Any time",
              "Last month",
              "Last 3 months",
              "Last 6 months",
              "Last year"
            ],
            "default": "any",
            "editor": "select"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only keep reviews written on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). With newest-first order, reading stops once reviews are older.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Skip reviews delivered by earlier runs that used the same state key, and do not charge for them. Forces newest-first order.",
            "default": false
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the incremental state (letters, digits and dashes). Change it to start over. Example: weekly-monitoring.",
            "default": "default",
            "editor": "textfield"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add one row per product that could not be read, with the reason. These rows are free.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per product or source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "type": "string",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "type": "string",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "type": "object",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "type": "null",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "type": "null",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "type": "null",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "type": "boolean",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "type": "string",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "reviewId": "TrustRadius___638fcc80649f691b7696d9c6",
          "platform": "trustradius",
          "productName": "Asana",
          "date": "2022-12-15",
          "title": "Great task management tool",
          "text": "Asana is used to manage all the internal activities at my company...",
          "pros": "Receive requests\nDocument information\nManage tasks across departments",
          "cons": "More free/not-premium features\nSmall navigation bugs\nTask templates",
          "ratingOverall": 5,
          "ratings": {
            "likelihoodToRecommend": 10
          },
          "reviewerRole": null,
          "reviewerCompanySize": null,
          "reviewerTimeUsed": null,
          "incentive": "TrustRadius",
          "incentivized": true,
          "extras": {
            "jobType": "Manager",
            "department": "Marketing",
            "ratingRaw10": 10
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.002,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:41:07.000Z"
    },
    {
      "slug": "software-reviews",
      "family": "reviews",
      "title": "Software Reviews Scraper: Capterra, Google Play, App Store",
      "description": "Type a product name like \"Slack\" or paste a mix of links and get its reviews from Capterra (and optionally Software Advice), Google Play and the App Store in one dataset, one row shape: rating, title, text, pros, cons, date, the reviewer's role and company size where the site has them.",
      "inputSchema": {
        "title": "Software Reviews Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "products": {
            "title": "Products (by name)",
            "type": "array",
            "description": "Software product names, e.g. Slack or Asana. Each name is looked up on Capterra / Software Advice and in the Google Play and App Store catalogs. An app is used only when it is confidently the same product; otherwise that source returns a free 'no_match' row.",
            "editor": "stringList",
            "items": {
              "type": "string"
            },
            "prefill": [
              "Slack"
            ]
          },
          "startUrls": {
            "title": "Start URLs (optional)",
            "type": "array",
            "description": "Product pages from Capterra, Software Advice, GetApp, Google Play or the App Store. Each link is routed to its source by host. A Google Play package id (com.slack) or an App Store id (618783545) also works. Can be combined with product names. Example: https://www.capterra.com/p/135003/Slack/.",
            "editor": "requestListSources"
          },
          "sources": {
            "title": "Sources for product names",
            "type": "array",
            "description": "Which sources to search when you give product names. Capterra, Google Play and App Store are on by default. Software Advice shows largely the same reviews as Capterra, so it is opt-in; duplicates are dropped and never charged. Links in 'Start URLs' are always read, whatever is chosen here. Default: capterra, googleplay, appstore.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "capterra",
                "softwareadvice",
                "googleplay",
                "appstore"
              ],
              "enumTitles": [
                "Capterra",
                "Software Advice",
                "Google Play",
                "App Store"
              ]
            },
            "prefill": [
              "capterra",
              "googleplay",
              "appstore"
            ]
          },
          "maxReviewsPerSource": {
            "title": "Max reviews per source",
            "type": "integer",
            "description": "Maximum number of reviews to collect from each source for each product (up to 50,000). Default: 100.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 9
          },
          "sort": {
            "title": "Sort order",
            "type": "string",
            "description": "Most recent first, or most helpful first (each store's own relevance ranking). The names most_recent, most_helpful, highest_rated and lowest_rated from similar actors also work; the two rating sorts are not supported and use Most recent.",
            "enum": [
              "recent",
              "helpful",
              "most_recent",
              "most_helpful",
              "highest_rated",
              "lowest_rated"
            ],
            "enumTitles": [
              "Most recent",
              "Most helpful",
              "Most recent (alias most_recent)",
              "Most helpful (alias most_helpful)",
              "Highest rated (falls back to Most recent)",
              "Lowest rated (falls back to Most recent)"
            ],
            "default": "recent",
            "editor": "select"
          },
          "appCountry": {
            "title": "App store country",
            "type": "string",
            "description": "Two-letter storefront country for Google Play and the App Store, e.g. us, gb, de. Leave empty for us (or the country in an App Store link).",
            "editor": "textfield"
          },
          "language": {
            "title": "Review language",
            "type": "string",
            "description": "Language of the Google Play reviews, e.g. en or pt-BR.",
            "default": "en",
            "editor": "textfield"
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only keep reviews published on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). With 'Most recent' sorting, paging stops once reviews are older.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Return only reviews not delivered by earlier runs that used the same state key. Uses the 'Most recent' sort order. Skipped reviews are not charged.",
            "default": false,
            "editor": "checkbox"
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the incremental state used by 'Only new reviews' (letters, digits and dashes, up to 40 characters). Use different keys for independent schedules. Example: weekly-monitoring.",
            "default": "default",
            "editor": "textfield"
          },
          "includeSummaryRows": {
            "title": "Include summary rows",
            "type": "boolean",
            "description": "Add one row with rowType 'summary' (not charged) per product: the rating and review count each source shows, and how confident the match is.",
            "default": true,
            "editor": "checkbox"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add one row with rowType 'error' (not charged) for each input or source that could not be scraped, e.g. no confident app match.",
            "default": true,
            "editor": "checkbox"
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per product or source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'summary': one row per product with the ratings each source shows (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "null",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "type": "string",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "type": "null",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        },
        {
          "name": "inputType",
          "description": "Summary rows: whether the input was a product 'name' or a URL."
        },
        {
          "name": "vendor",
          "description": "Summary rows: vendor name of the matched product."
        },
        {
          "name": "matchConfidence",
          "description": "Summary rows: how sure the name match is per source, e.g. { capterra: 'high', googleplay: 'high' }."
        },
        {
          "name": "sources",
          "description": "Summary rows: per source { name, rating, reviewCount, status, reviewsPushed }."
        }
      ],
      "sampleRows": [
        {
          "rowType": "review",
          "platform": "googleplay",
          "reviewId": "GooglePlay___a1b2c3d4-0000-0000-0000-000000000000",
          "productName": "Slack",
          "date": "2026-10-14",
          "title": null,
          "text": "Notifications are finally reliable on my phone.",
          "ratingOverall": 4,
          "reviewerCompanySize": null,
          "extras": {
            "inputProduct": "Slack"
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.0015,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:14:10.000Z"
    },
    {
      "slug": "hotel-reputation",
      "family": "reviews",
      "title": "Hotel Reviews Scraper: Tripadvisor, Booking.com, Google",
      "description": "Give it a hotel name or its Tripadvisor, Booking.com, Google Hotels, Agoda and Trip.com links and it returns what guests say on every site in one dataset: one row per review with the rating on one 1-5 scale (the original kept), text, date and hotel reply, plus a free summary row per hotel with each site's rating, review counts and category scores compared with the city.",
      "inputSchema": {
        "title": "Hotel Reputation (Tripadvisor + Booking) input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "hotels": {
            "title": "Hotels",
            "type": "array",
            "description": "Hotel names, or objects that tie one hotel's pages together. A plain name is looked up on Tripadvisor (and Google Hotels with 'Also search Google by name'); Booking, Agoda and Trip.com have no name search, so for them or Google use the object form, for example {\"name\": \"The Savoy\", \"bookingUrl\": \"https://www.booking.com/hotel/gb/the-savoy.html\"}. Object fields: name, tripadvisorUrl, bookingUrl, bookingId, googleUrl, agodaUrl, agodaId, tripcomUrl, tripcomId.",
            "editor": "json",
            "prefill": [
              {
                "name": "The Savoy London",
                "bookingUrl": "https://www.booking.com/hotel/gb/the-savoy.html"
              }
            ]
          },
          "startUrls": {
            "title": "Hotel links (optional)",
            "type": "array",
            "description": "Tripadvisor, Booking.com, Agoda or Trip.com hotel pages, or Google Hotels pages. Each link goes to its site by host and counts as its own hotel; use the object form of 'Hotels' to pair pages into one summary. Example: https://www.booking.com/hotel/gb/the-savoy.html. A `startUrls` list written for a single-site Tripadvisor, Booking or Google reviews actor works here unchanged.",
            "editor": "requestListSources"
          },
          "bookingHotelIds": {
            "title": "Booking hotel IDs (optional)",
            "type": "array",
            "description": "Numeric Booking.com hotel IDs. Rows from an ID carry no hotel name or Booking rating, so prefer the hotel link when you have it.",
            "editor": "json",
            "default": []
          },
          "searchGoogleByName": {
            "title": "Also search Google by name",
            "type": "boolean",
            "description": "Also look each hotel name up on Google Hotels and read Google's own reviews: one more source per hotel, so up to twice the reviews (and cost) per name. Off by default; Google links in the object form or in the hotel links are always read.",
            "default": false,
            "editor": "checkbox"
          },
          "maxReviewsPerSource": {
            "title": "Max reviews per source",
            "type": "integer",
            "description": "Maximum number of reviews to collect from each site for each hotel (up to 50,000). Reviews are always read newest first. Booking shows about the last three years. Also accepted as `maxItemsPerQuery`, `maxReviewsPerHotel` or `maxReviews` (other hotel review actors). Default 100.",
            "minimum": 1,
            "maximum": 50000,
            "prefill": 5
          },
          "ratings": {
            "title": "Ratings (stars)",
            "type": "array",
            "description": "Only reviews with these star ratings (1 to 5). Tripadvisor filters on the site. Booking scores are converted to stars first (score divided by 2, rounded) and filtered after download. Leave empty for all. Also accepted as `reviewRatings` (Tripadvisor actor values, ALL_REVIEW_RATINGS = all).",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "1",
                "2",
                "3",
                "4",
                "5"
              ],
              "enumTitles": [
                "1 star",
                "2 stars",
                "3 stars",
                "4 stars",
                "5 stars"
              ]
            }
          },
          "languages": {
            "title": "Review languages",
            "type": "array",
            "description": "Two-letter language codes such as en or de. Applied to every source. Leave empty for all. Also accepted as `reviewsLanguages` (ALL_REVIEW_LANGUAGES = all).",
            "editor": "stringList",
            "items": {
              "type": "string"
            }
          },
          "travelerTypes": {
            "title": "Traveler types",
            "type": "array",
            "description": "Only reviews from these kinds of travelers, applied to every source (Tripadvisor trip type, Booking guest type, Google, Agoda and Trip.com traveler type). Leave empty for all.",
            "editor": "select",
            "items": {
              "type": "string",
              "enum": [
                "BUSINESS",
                "COUPLES",
                "FAMILY",
                "FRIENDS",
                "SOLO"
              ],
              "enumTitles": [
                "Business",
                "Couples",
                "Families",
                "Friends",
                "Solo"
              ]
            },
            "default": []
          },
          "dateFrom": {
            "title": "Reviews from date",
            "type": "string",
            "description": "Only keep reviews published on or after this date (YYYY-MM-DD, or a relative span such as 30 days or 2 months; empty = no filter). Paging stops once reviews are older. Also accepted as `lastReviewDate`, `cutoffDate`, `reviewsStartDate` or `reviewsFromDate`.",
            "editor": "textfield"
          },
          "onlyNewReviews": {
            "title": "Only new reviews",
            "type": "boolean",
            "description": "Return only reviews not delivered by earlier runs that used the same state key. Skipped reviews are not charged.",
            "default": false,
            "editor": "checkbox"
          },
          "stateKey": {
            "title": "State key",
            "type": "string",
            "description": "Name of the incremental state used by 'Only new reviews' (letters, digits and dashes, up to 40 characters). Use different keys for independent schedules. Example: weekly-monitoring.",
            "default": "default",
            "editor": "textfield"
          },
          "includeSummary": {
            "title": "Include hotel summary rows",
            "type": "boolean",
            "description": "Add one row with rowType 'hotel-summary' (not charged) per hotel: the Tripadvisor rating and review count, the Booking score and count with Booking's category averages compared with the city, and the Google rating with its topic breakdown. Costs a few extra small requests per hotel.",
            "default": true,
            "editor": "checkbox"
          },
          "includeErrorRows": {
            "title": "Include error rows",
            "type": "boolean",
            "description": "Add one row with rowType 'error' (not charged) for each input or site that could not be read, e.g. no confident Tripadvisor match.",
            "default": true,
            "editor": "checkbox"
          },
          "includeCoverageRows": {
            "title": "Include coverage rows",
            "type": "boolean",
            "description": "Add one free row with rowType 'coverage' per hotel and source: the review total the site states, how many were delivered, why paging ended and whether anything is missing. Not charged.",
            "default": true,
            "editor": "checkbox"
          },
          "markEdited": {
            "title": "Mark edited reviews",
            "type": "boolean",
            "description": "With 'Only new reviews': also deliver reviews seen before whose text changed in the last 7 days, marked extras.changeType 'edited'. Edited reviews are charged like new ones.",
            "default": true,
            "editor": "checkbox"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "description": "Kind of row. Filter on this first. Values: 'review': one review (charged). 'hotel-summary': one row per hotel with its overall ratings (free). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'."
        },
        {
          "name": "productName",
          "type": "string",
          "description": "Name of the reviewed hotel, business, product or app."
        },
        {
          "name": "productUrl",
          "description": "Page of the reviewed hotel, business, product or app on the source site."
        },
        {
          "name": "entityId",
          "description": "The site's own id of the reviewed entity (place id, product id, app id)."
        },
        {
          "name": "reviewId",
          "type": "string",
          "description": "Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs."
        },
        {
          "name": "reviewIdSource",
          "description": "'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable)."
        },
        {
          "name": "reviewUrl",
          "description": "Link to the single review, null when the site has none."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Review headline, null when the site has none."
        },
        {
          "name": "text",
          "description": "Review text in the language it was written in. Null when the review has no text."
        },
        {
          "name": "pros",
          "type": "string",
          "description": "What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise."
        },
        {
          "name": "cons",
          "type": "string",
          "description": "What the reviewer disliked, for sites that ask separately. Null otherwise."
        },
        {
          "name": "ratingOverall",
          "type": "number",
          "description": "Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing."
        },
        {
          "name": "ratings",
          "description": "Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none."
        },
        {
          "name": "date",
          "type": "string",
          "description": "Date the review was written, ISO format yyyy-mm-dd. Null if unknown."
        },
        {
          "name": "reviewerRole",
          "description": "Job title or role the reviewer states. No names. Null if not given."
        },
        {
          "name": "reviewerIndustry",
          "description": "Industry the reviewer states. Null if not given."
        },
        {
          "name": "reviewerCompanySize",
          "description": "Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given."
        },
        {
          "name": "reviewerTimeUsed",
          "description": "How long the reviewer used the product, as the site words it. Null if not given."
        },
        {
          "name": "switchedFrom",
          "description": "Product the reviewer switched from, null if not given."
        },
        {
          "name": "alternativesConsidered",
          "description": "Alternatives the reviewer considered, comma-separated, null if not given."
        },
        {
          "name": "reasonForChoosing",
          "description": "Why the reviewer chose this product, free text, null if not given."
        },
        {
          "name": "reasonForSwitching",
          "description": "Why the reviewer left the previous product, free text, null if not given."
        },
        {
          "name": "adviceToOthers",
          "description": "Advice the reviewer gives others (Glassdoor: advice to management), null if not given."
        },
        {
          "name": "incentivized",
          "description": "True when the site marks the review as incentivized (for example a gift card). Null when the site does not say."
        },
        {
          "name": "incentive",
          "description": "The site's raw incentive label, null if none."
        },
        {
          "name": "reviewerVerified",
          "description": "True when the site marks the review as verified. Null when the site does not say."
        },
        {
          "name": "vendorResponse",
          "description": "Reply of the owner, vendor, developer or employer, null when there is none."
        },
        {
          "name": "helpfulVotes",
          "description": "Number of helpful or useful votes, null when the site shows none."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language of the review text as a two-letter code, null if unknown."
        },
        {
          "name": "country",
          "type": "string",
          "description": "Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data."
        },
        {
          "name": "product",
          "description": "Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures."
        },
        {
          "name": "extras",
          "type": "object",
          "description": "Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total the site states for the entity, null when it does not say."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: number of rows delivered for the entity in this run."
        },
        {
          "name": "skippedSeen",
          "description": "Coverage rows: items skipped because an earlier run already delivered them (only-new mode)."
        },
        {
          "name": "duplicates",
          "description": "Coverage rows: repeats the site returned that were dropped."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active)."
        },
        {
          "name": "capSource",
          "description": "Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer rows than the site states with no maximum or filter set, an error, or the site's paging changed during the run. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure), unstable_paging (the site changed its list while it was read, so rows inside the range read may be missing even though your maximum or date ended the run). Null when shortfall is false."
        },
        {
          "name": "pagingGaps",
          "description": "Coverage rows: number of pages the site served inconsistently that could not be repaired by re-reading. Above 0 means shortfallReason unstable_paging."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        },
        {
          "name": "hotelName",
          "description": "'hotel-summary' rows: the hotel name."
        },
        {
          "name": "tripadvisor",
          "description": "'hotel-summary' rows: Tripadvisor rating 1-5, review count, reviews per star and how the hotel was matched."
        },
        {
          "name": "booking",
          "description": "'hotel-summary' rows: Booking score10, rating (score / 2), review count, stars, city and categoryScores with the city range and vsCity (above, within, below)."
        },
        {
          "name": "google",
          "description": "'hotel-summary' rows: Google Hotels rating 1-5, number of Google reviews, reviews per star, topic breakdown (mentions, positive, negative per topic) and how the hotel was matched."
        },
        {
          "name": "agoda",
          "description": "'hotel-summary' rows: Agoda's own guests: score10, rating (score / 2), number of ratings, stars, city, categoryScores with the city average, and the combined score of every provider Agoda shows."
        },
        {
          "name": "tripcom",
          "description": "'hotel-summary' rows: Trip.com score10, rating (score / 2), review count, category scores (location, facilities, service, cleanliness), positive and negative counts and the advertised from-price."
        },
        {
          "name": "combined",
          "description": "'hotel-summary' rows: the ratings of all requested sites weighted by review count, when two or more sites were read."
        }
      ],
      "sampleRows": [
        {
          "platform": "booking",
          "reviewId": "Booking___c6397d1e2a854aa3",
          "productName": "The Savoy",
          "date": "2026-09-30",
          "title": "Wonderful stay",
          "pros": "Staff and location.",
          "cons": "Breakfast queue.",
          "ratingOverall": 4.5,
          "language": "en",
          "country": "US",
          "extras": {
            "rawRating": 9,
            "rawScale": 10,
            "score10": 9,
            "travelerType": "COUPLES"
          }
        }
      ],
      "prices": {
        "events": {
          "review": {
            "usd": 0.001,
            "title": "Review"
          }
        },
        "primaryEvent": "review",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:14:10.000Z"
    },
    {
      "slug": "capterra-products",
      "family": "reviews",
      "title": "Capterra Products Scraper: Pricing, Features, Rank",
      "description": "Give it Capterra category or product pages and it returns one JSON row per software product: name, vendor, rating, review count, starting price and pricing plans, top features, category rank and the sponsored flag.",
      "inputSchema": {
        "title": "Capterra Products Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Category or product URLs",
            "type": "array",
            "description": "Capterra category pages (https://www.capterra.com/project-management-software/) list every product in the category. Product pages (https://www.capterra.com/p/184581/Asana-PM/) return that one product. Both kinds can be mixed. Give at least one. Other Capterra actors' field `urls` (plain list of links) is accepted too.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.capterra.com/project-management-software/"
              }
            ]
          },
          "maxResultsPerCategory": {
            "title": "Max products per category",
            "type": "integer",
            "description": "Maximum number of products to collect from each category page list (up to 1,000). A product URL always counts as one. Also accepted as `maxItems`, or as `maxPages` (25 products a page). Default 50.",
            "minimum": 1,
            "maximum": 1000,
            "prefill": 50
          },
          "includePricingDetails": {
            "title": "Look up exact pricing and features",
            "type": "boolean",
            "description": "Category lists only show whether a free trial or free version exists. With this on, each product's own page is read too, which adds the starting price, pricing model and a ranked feature list. It makes runs slower and heavier on data, but the price per result stays the same.",
            "default": true
          },
          "includeErrorRows": {
            "title": "Add a row for inputs that failed",
            "type": "boolean",
            "description": "Adds a free row to the dataset for every URL that could not be read, so a failure is never silent.",
            "default": true
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'product': one product (charged). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source site, e.g. 'capterra'."
        },
        {
          "name": "seoId",
          "type": "string",
          "description": "Capterra product id, the number in the product URL."
        },
        {
          "name": "name",
          "type": "string",
          "description": "Product name."
        },
        {
          "name": "url",
          "description": "Product page URL."
        },
        {
          "name": "vendor",
          "type": "string",
          "description": "Vendor company name."
        },
        {
          "name": "rating",
          "type": "number",
          "description": "Overall rating, 1 to 5."
        },
        {
          "name": "reviewCount",
          "type": "number",
          "description": "Number of reviews the site shows."
        },
        {
          "name": "category",
          "type": "string",
          "description": "Category the product was listed under (for a direct product URL: its primary category)."
        },
        {
          "name": "primaryCategory",
          "description": "The product's own primary category from its page, which can differ from the listing (null when includePricingDetails is off)."
        },
        {
          "name": "description",
          "description": "Short product description."
        },
        {
          "name": "pricing",
          "type": "object",
          "description": "Pricing summary: startingPrice, currency, model (e.g. Per User), frequency (e.g. Per Month), hasFreeTrial, hasFreeVersion, summary text and plans [{ name, startingPrice }]. Fields the site does not publish are null."
        },
        {
          "name": "topFeatures",
          "type": "array",
          "description": "Most mentioned features listed by the site."
        },
        {
          "name": "rank",
          "type": "number",
          "description": "1-based position in the category list, null for direct product URLs."
        },
        {
          "name": "sponsored",
          "type": "boolean",
          "description": "True when the listing is a paid placement, null when the site does not say."
        },
        {
          "name": "page",
          "description": "Number of the list page the product was found on."
        },
        {
          "name": "categoryUrl",
          "description": "Category URL the product was listed under, null for direct product URLs."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "rowType": "product",
          "platform": "capterra",
          "seoId": "184581",
          "name": "Asana",
          "vendor": "Asana",
          "rating": 4.49,
          "reviewCount": 13770,
          "category": "Project Management",
          "pricing": {
            "startingPrice": 10.99,
            "currency": "USD",
            "model": "Per User",
            "frequency": "Per Month",
            "hasFreeTrial": true,
            "hasFreeVersion": true,
            "summary": "From $10.99 (Per User, Per Month)"
          },
          "topFeatures": [
            "Task Management",
            "Collaboration Tools"
          ],
          "rank": 18,
          "sponsored": false
        }
      ],
      "prices": {
        "events": {
          "result": {
            "usd": 0.002,
            "title": "Product"
          }
        },
        "primaryEvent": "result",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:20:00.000Z"
    },
    {
      "slug": "reputation-monitor",
      "family": "reviews",
      "title": "Review Monitor: Negative Review Alerts and Rating Drift",
      "description": "Give it a watchlist of hotels, local businesses, apps or SaaS products (their pages on Booking.com, Agoda, Trip.com, Tripadvisor, Google Maps, Google Hotels, Yelp, Google Play, the App Store, Capterra or TrustRadius) and every run returns what changed since the last one: an alert row per new 1★ or 2★ review and a summary row per entity with rating drift, 30-day vs lifetime average, owner-reply rate and lag, review velocity and, for apps, per-version rating regressions.",
      "inputSchema": {
        "title": "Reputation Monitor input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Entities to watch (URLs)",
            "type": "array",
            "description": "Example: [\"https://www.booking.com/hotel/gb/the-savoy.html\", \"https://play.google.com/store/apps/details?id=com.Slack\"]. One URL per hotel, business, app or product. Sources: Booking.com, Agoda or Trip.com hotel, Tripadvisor place, Google Hotels hotel (google.com/travel/hotels/entity/...), Google Maps place (a place link, maps.app.goo.gl link or maps.google.com/?cid=...), Google Play app, App Store app, Yelp business, Capterra product, TrustRadius product. Max 2,000. Required.",
            "editor": "stringList",
            "prefill": [
              "https://www.booking.com/hotel/gb/the-savoy.html",
              "https://www.yelp.com/biz/katzs-delicatessen-new-york",
              "https://apps.apple.com/us/app/slack/id618783545"
            ]
          },
          "stateKey": {
            "title": "Watchlist name",
            "type": "string",
            "description": "Example: `hotels-london`. Names the saved state (1-40 letters, digits or dashes), so several watchlists run side by side. Each run is compared with the last run of the same name. A new name starts a new baseline.",
            "editor": "textfield",
            "default": "default",
            "pattern": "^[A-Za-z0-9-]{1,40}$"
          },
          "alertMaxRating": {
            "title": "Alert at or below (stars, 1-5 scale)",
            "type": "integer",
            "description": "Example: 2 = one alert row per new 1★ or 2★ review. Allowed 1-4. Booking, Agoda, Trip.com and TrustRadius 1-10 scores are halved to the 1-5 scale first.",
            "default": 2,
            "minimum": 1,
            "maximum": 4
          },
          "maxReviewsPerEntity": {
            "title": "Max reviews read per entity (reviews per run)",
            "type": "integer",
            "description": "Example: 100. Newest reviews read per entity and run; the first run uses them as history for the 30-day and reply metrics. Later runs stop at the last seen review, usually after one page. Raise it for apps with hundreds of reviews a day. 10-2,000. Also accepted as `maxReviews`, `maxItemsPerQuery` or `maxReviewsPerHotel` (other review actors; clamped to 10-2,000). Default 100.",
            "minimum": 10,
            "maximum": 2000,
            "prefill": 50
          },
          "language": {
            "title": "Google Play review language",
            "type": "string",
            "description": "Example: `en`. Google Play shows reviews per language (code such as en, de, pt-BR). Other sources return every language.",
            "editor": "textfield",
            "default": "en"
          },
          "country": {
            "title": "App store country",
            "type": "string",
            "description": "Example: `us`. Two-letter storefront for Google Play, and for App Store URLs without a country.",
            "editor": "textfield",
            "default": "us"
          },
          "includeErrorRows": {
            "title": "Add a row for entities that failed",
            "type": "boolean",
            "description": "Default on. Adds a free `error` row (code + message) for every URL that could not be read or is not supported, so a failure is never silent.",
            "default": true,
            "sectionCaption": "Output"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. 'alert': one new (or newly lowered) review at or below the alert rating (charged). 'summary': one per entity and run with drift, reply and velocity metrics (charged as an entity check, or as a cheaper baseline on the first run). 'error': a URL that could not be read or is not supported; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Source: booking, tripadvisor, googleplay, appstore, yelp, capterra or trustradius."
        },
        {
          "name": "entityId",
          "type": "string",
          "description": "Native id of the entity on its source (hotel id, place id, app id, business id, product id)."
        },
        {
          "name": "input",
          "type": "string",
          "description": "The URL you entered for this entity."
        },
        {
          "name": "checkedAt",
          "type": "string",
          "description": "ISO timestamp of this run's check."
        },
        {
          "name": "entityName",
          "description": "Alert rows: name of the hotel, business, app or product."
        },
        {
          "name": "entityUrl",
          "description": "Alert rows: public page of the entity."
        },
        {
          "name": "reviewId",
          "description": "Alert rows: stable review id '<Platform>___<native id>'."
        },
        {
          "name": "reviewUrl",
          "description": "Alert rows: public link to the review, null when the source has none."
        },
        {
          "name": "date",
          "description": "Alert rows: review date (YYYY-MM-DD)."
        },
        {
          "name": "title",
          "description": "Alert rows: review title, null when the source has none. Personal data is redacted."
        },
        {
          "name": "text",
          "description": "Alert rows: review text. Emails, phone numbers and names in greetings are redacted."
        },
        {
          "name": "pros",
          "description": "Alert rows: what the reviewer liked (Booking, Capterra, TrustRadius), null elsewhere."
        },
        {
          "name": "cons",
          "description": "Alert rows: what the reviewer disliked (Booking, Capterra, TrustRadius), null elsewhere."
        },
        {
          "name": "language",
          "description": "Alert rows: review language code when the source states it."
        },
        {
          "name": "appVersion",
          "description": "Alert rows: app version the review was written for (app stores), null elsewhere."
        },
        {
          "name": "ownerReplied",
          "description": "Alert rows: true when the owner or developer has already replied."
        },
        {
          "name": "ownerReply",
          "description": "Alert rows: the owner's reply text (redacted), null when there is none."
        },
        {
          "name": "changeType",
          "description": "Alert rows: 'new' = first seen this run; 'edited' = a known review whose rating was edited down into the alert band."
        },
        {
          "name": "status",
          "type": "string",
          "description": "Summary rows: 'baseline' (first run, history recorded, no alerts), 'changed' (new or edited reviews) or 'unchanged'."
        },
        {
          "name": "name",
          "type": "string",
          "description": "Summary rows: name of the hotel, business, app or product."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Summary rows: public page of the entity."
        },
        {
          "name": "previousCheckAt",
          "type": "string",
          "description": "Summary rows: ISO timestamp of the previous run with this watchlist name, null on a baseline."
        },
        {
          "name": "rating",
          "type": "number",
          "description": "Rating on 1-5 (Booking and TrustRadius 1-10 scores are halved) as the source states it now. Alert rows: the review's rating."
        },
        {
          "name": "previousRating",
          "type": "number",
          "description": "Summary rows: the source's average rating at the previous run."
        },
        {
          "name": "ratingChange",
          "type": "number",
          "description": "Summary rows: rating minus previousRating (stars). Negative = the average went down."
        },
        {
          "name": "reviewCount",
          "type": "number",
          "description": "Summary rows: review count the source states (app stores: the number of star ratings)."
        },
        {
          "name": "previousReviewCount",
          "type": "number",
          "description": "Summary rows: reviewCount at the previous run."
        },
        {
          "name": "reviewCountChange",
          "type": "number",
          "description": "Summary rows: reviewCount minus previousReviewCount, the true number of new reviews/ratings even beyond the read limit."
        },
        {
          "name": "newReviews",
          "type": "number",
          "description": "Summary rows: reviews first seen this run (0 on a baseline)."
        },
        {
          "name": "editedReviews",
          "type": "number",
          "description": "Summary rows: known reviews whose text or rating changed since they were seen."
        },
        {
          "name": "newNegativeReviews",
          "type": "number",
          "description": "Summary rows: alert rows written for this entity this run."
        },
        {
          "name": "newReviewsAvgRating",
          "type": "number",
          "description": "Summary rows: average rating of this run's new and edited reviews, null when there are none."
        },
        {
          "name": "avgRating30d",
          "type": "number",
          "description": "Summary rows: average rating of the reviews dated in the last 30 days (from the remembered history)."
        },
        {
          "name": "previousAvgRating30d",
          "type": "number",
          "description": "Summary rows: avgRating30d at the previous run."
        },
        {
          "name": "reviews7d",
          "type": "number",
          "description": "Summary rows: reviews dated in the last 7 days."
        },
        {
          "name": "reviews30d",
          "type": "number",
          "description": "Summary rows: reviews dated in the last 30 days (a lower bound when windowComplete30d is false)."
        },
        {
          "name": "reviewsPrior30d",
          "description": "Summary rows: reviews dated 31-60 days ago, to compare velocity."
        },
        {
          "name": "reviewsPerWeek",
          "type": "number",
          "description": "Summary rows: review velocity, reviews30d per week."
        },
        {
          "name": "avgRatingLifetime",
          "type": "number",
          "description": "Summary rows: lifetime average: the source's stated rating, or for app stores (whose rating counts silent star ratings) the average of the remembered written reviews."
        },
        {
          "name": "lifetimeSource",
          "type": "string",
          "description": "Summary rows: 'site' (the source's stated average) or 'sample' (average of the remembered reviews)."
        },
        {
          "name": "rating30dVsLifetime",
          "type": "number",
          "description": "Summary rows: avgRating30d minus avgRatingLifetime. Negative = recent reviews are worse than usual."
        },
        {
          "name": "ownerReplyRate90d",
          "type": "number",
          "description": "Summary rows: share (0-1) of the last 90 days' reviews with an owner reply; null on TrustRadius (no replies)."
        },
        {
          "name": "replySample90d",
          "description": "Summary rows: number of reviews ownerReplyRate90d is based on."
        },
        {
          "name": "medianReplyLagDays",
          "type": "number",
          "description": "Summary rows: median days from review to owner reply (Tripadvisor, Yelp, app stores); null where the source does not date replies."
        },
        {
          "name": "replyLagSample",
          "description": "Summary rows: number of replies medianReplyLagDays is based on."
        },
        {
          "name": "unansweredNegative30d",
          "type": "number",
          "description": "Summary rows: reviews of the last 30 days at or below the alert rating without an owner reply."
        },
        {
          "name": "appVersions",
          "type": "array",
          "description": "Summary rows (apps): up to 5 newest versions in the history: [{ version, reviews, avgRating, firstSeen }]."
        },
        {
          "name": "versionRegression",
          "type": "null",
          "description": "Summary rows (apps): { version, avgRating, reviews, previousVersion, previousAvgRating, previousReviews, delta } when the newest version (5+ reviews) averages 0.5+ stars below the one before; else null."
        },
        {
          "name": "windowComplete30d",
          "type": "boolean",
          "description": "Summary rows: true when the history reaches back 30 days, so the 30-day figures are exact; false = based on the newest reviews read."
        },
        {
          "name": "windowComplete60d",
          "description": "Summary rows: same for 60 days (reviewsPrior30d)."
        },
        {
          "name": "historyReviews",
          "type": "number",
          "description": "Summary rows: reviews remembered for this entity (facts only: date, rating, reply; no text)."
        },
        {
          "name": "historySince",
          "type": "string",
          "description": "Summary rows: date of the oldest remembered review."
        },
        {
          "name": "stopReason",
          "type": "string",
          "description": "Summary rows: why reading stopped: 'onlyNew' (reached reviews seen before), 'limit' (max reviews per entity), 'end' (no more reviews), 'budget' or 'error'."
        },
        {
          "name": "flags",
          "type": "array",
          "description": "Summary rows: what needs attention: new_negative, rating_drop, recent_below_lifetime, version_regression, unanswered_negative, velocity_spike. Empty on a baseline."
        },
        {
          "name": "code",
          "description": "Error rows: invalid_input, not_found, blocked, unavailable or no_match."
        },
        {
          "name": "message",
          "description": "Error rows: what went wrong, in plain words."
        },
        {
          "name": "scrapedAt",
          "description": "Error rows: ISO timestamp of the failure."
        }
      ],
      "sampleRows": [
        {
          "rowType": "summary",
          "status": "changed",
          "platform": "appstore",
          "entityId": "618783545-us",
          "input": "https://apps.apple.com/us/app/slack/id618783545",
          "name": "Slack",
          "url": "https://apps.apple.com/us/app/slack/id618783545",
          "checkedAt": "2026-10-21T22:59:28.653Z",
          "previousCheckAt": "2026-10-21T22:54:50.343Z",
          "rating": 4.14,
          "previousRating": 4.14,
          "ratingChange": 0,
          "reviewCount": 54143,
          "previousReviewCount": 54115,
          "reviewCountChange": 28,
          "newReviews": 6,
          "editedReviews": 0,
          "newNegativeReviews": 1,
          "newReviewsAvgRating": 4.5,
          "avgRating30d": 3.29,
          "previousAvgRating30d": 3.29,
          "reviews7d": 55,
          "reviews30d": 100,
          "reviewsPerWeek": 23.3,
          "avgRatingLifetime": 3.29,
          "lifetimeSource": "sample",
          "rating30dVsLifetime": 0,
          "ownerReplyRate90d": 0.05,
          "medianReplyLagDays": 0,
          "unansweredNegative30d": 35,
          "appVersions": [],
          "versionRegression": null,
          "windowComplete30d": false,
          "historyReviews": 100,
          "historySince": "2026-10-08",
          "stopReason": "onlyNew",
          "flags": [
            "new_negative",
            "unanswered_negative"
          ]
        }
      ],
      "prices": {
        "events": {
          "entity-check": {
            "usd": 0.01,
            "title": "Entity checked"
          },
          "alert": {
            "usd": 0.0015,
            "title": "Negative review alert"
          },
          "baseline": {
            "usd": 0.009,
            "title": "Baseline recorded"
          }
        },
        "primaryEvent": "entity-check",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T20:15:48.000Z"
    },
    {
      "slug": "1688-products",
      "family": "ecommerce",
      "title": "1688 Products Scraper: Wholesale Prices, MOQ, Suppliers",
      "description": "Find factory suppliers and wholesale prices for a product idea, compare them with your retail price, or watch a supplier's offers. Type a keyword in Chinese or English, or paste 1688 offer links or ids, and get wholesale products from 1688.com as one JSON row per product: price tiers by quantity, minimum order, units sold, repurchase rate, supplier and factory data.",
      "inputSchema": {
        "title": "1688 Products Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "keywords": {
            "title": "Search keywords",
            "type": "array",
            "description": "Words to search 1688.com for, Chinese or English, for example [\"保温杯\", \"water bottle\"]. Each keyword is searched on its own and every product keeps the keyword and its position in the results. Leave empty when you give offer ids or links. Also read from queries.",
            "editor": "stringList",
            "prefill": [
              "保温杯"
            ]
          },
          "maxItemsPerKeyword": {
            "title": "Max products per keyword",
            "type": "integer",
            "description": "How many products to return per keyword, for example 120. Default: 60. A results page holds 60 products and 1688 shows at most 2,000 per search, so 2,000 is the ceiling. Also read from maxResults or maxProducts (other 1688 actors' names).",
            "minimum": 1,
            "maximum": 2000,
            "prefill": 50
          },
          "sort": {
            "title": "Sort by",
            "type": "string",
            "description": "Order of the results: best match (default), most sold in the last 30 days, or price. Example: priceAsc. Also read from sortBy (bestSelling = sales) or sortType (va_rmdarkgmv30 = sales).",
            "enum": [
              "relevance",
              "sales",
              "priceAsc",
              "priceDesc"
            ],
            "enumTitles": [
              "Best match",
              "Most sold (30 days)",
              "Price, low to high",
              "Price, high to low"
            ],
            "editor": "select",
            "prefill": "relevance"
          },
          "minPrice": {
            "title": "Minimum price (CNY)",
            "type": "number",
            "description": "Only products priced at or above this amount in Chinese yuan, for example 10. Leave empty for no lower limit. Also read from priceMin.",
            "editor": "number"
          },
          "maxPrice": {
            "title": "Maximum price (CNY)",
            "type": "number",
            "description": "Only products priced at or below this amount in Chinese yuan, for example 50. Leave empty for no upper limit. Also read from priceMax.",
            "editor": "number"
          },
          "includeDetails": {
            "title": "Add product details to keyword results",
            "type": "boolean",
            "description": "Opens the product page of every keyword result and adds SKUs with stock, attributes, freight, ratings and the exact sold count. Costs more per product (the Product with details price) and runs slower. Offer ids and links always return details. Default: false. Also read from includeSkuDetails or enrichDetails.",
            "prefill": false
          },
          "offerIds": {
            "title": "Offer IDs",
            "type": "array",
            "description": "1688 offer ids (the number in the product link), for example [\"866095761513\"]. Each one returns a full product with details.",
            "editor": "stringList"
          },
          "urls": {
            "title": "Offer links",
            "type": "array",
            "description": "1688 product page links, for example https://detail.1688.com/offer/866095761513.html. The offer id is read from the link; mobile links work too.",
            "editor": "requestListSources"
          },
          "includeAds": {
            "title": "Include paid placements",
            "type": "boolean",
            "description": "Sponsored products are part of the search results. Each row has an isAd flag; turn this off to drop them (they still count towards the position). Example: true.",
            "default": true
          },
          "includeCoverageRows": {
            "title": "Add a coverage row per keyword",
            "type": "boolean",
            "description": "Adds one free row per keyword with the total 1688 reports, how many products were delivered and why the search stopped, so incomplete results are visible.",
            "default": true
          },
          "includeErrorRows": {
            "title": "Add a row for inputs that failed",
            "type": "boolean",
            "description": "Adds a free row to the dataset for every keyword or offer that returned nothing or could not be read, with a machine-readable code, so a failure is never silent.",
            "default": true
          },
          "searchGateWaitMinutes": {
            "title": "Wait for blocked searches (minutes)",
            "type": "integer",
            "description": "1688 sometimes refuses all searches for a few minutes. The run then waits up to this many minutes, trying again every 3 minutes, before the remaining keywords end as free error rows. 0 = do not wait.",
            "default": 12,
            "minimum": 0,
            "maximum": 30,
            "unit": "minutes"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. 'product': one 1688 offer (charged); 'coverage': per-keyword completeness (free); 'error': an input failed (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always '1688'."
        },
        {
          "name": "offerId",
          "type": "string",
          "description": "1688 offer id, the number in the product link. Stable key for joins and monitoring."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Product page link on detail.1688.com."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Product title as the seller wrote it, usually Chinese; contact data removed."
        },
        {
          "name": "keyword",
          "type": "string",
          "description": "Search keyword that found the offer; null for offers requested by id or link."
        },
        {
          "name": "rank",
          "type": "number",
          "description": "Position in the keyword's results, paid placements included; null for id lookups."
        },
        {
          "name": "isAd",
          "type": "boolean",
          "description": "True when the row is a paid placement in the search results."
        },
        {
          "name": "images",
          "description": "Product image links."
        },
        {
          "name": "currency",
          "type": "string",
          "description": "Currency of all prices, always CNY (Chinese yuan)."
        },
        {
          "name": "priceMin",
          "type": "number",
          "description": "Lowest unit price in CNY."
        },
        {
          "name": "priceMax",
          "type": "number",
          "description": "Highest unit price in CNY."
        },
        {
          "name": "priceTiers",
          "type": "array",
          "description": "Wholesale steps by quantity: {minQty, maxQty, price}. Empty when each variant is priced on its own."
        },
        {
          "name": "moq",
          "type": "number",
          "description": "Minimum order quantity."
        },
        {
          "name": "unit",
          "type": "string",
          "description": "Selling unit, e.g. 件 or 个."
        },
        {
          "name": "soldCount",
          "type": "number",
          "description": "Units sold; a lower bound on search rows, the exact total with details."
        },
        {
          "name": "soldText",
          "type": "string",
          "description": "Sold figure as 1688 shows it, e.g. '已售500+件'."
        },
        {
          "name": "soldScope",
          "type": "string",
          "description": "'offer' for this listing, 'network' for the whole product across the web."
        },
        {
          "name": "bookedCount",
          "description": "Number of orders shown on the search card."
        },
        {
          "name": "repurchaseRate",
          "type": "number",
          "description": "Share of buyers who reordered this offer, 0-100."
        },
        {
          "name": "origin",
          "type": "object",
          "description": "Dispatch location: province and city."
        },
        {
          "name": "serviceTags",
          "description": "Service badges, e.g. 深度验厂 (inspected factory) or 退货包运费."
        },
        {
          "name": "supplier",
          "type": "object",
          "description": "Supplier facts: companyName, memberId, isFactory, isSuperFactory, factoryInspected, yearsOnPlatform, serviceScores, repurchaseRate3m, shopUrl."
        },
        {
          "name": "skus",
          "description": "Details only: variants as {skuId, attrs, price, stock, sold}."
        },
        {
          "name": "attributes",
          "description": "Details only: product parameters as {name, value}, e.g. material or brand."
        },
        {
          "name": "freight",
          "description": "Details only: dispatch promise text, shipsFrom, free flag and an indicative fee in CNY."
        },
        {
          "name": "rating",
          "description": "Details only: buyer review score, goodRatePct and reviewCount."
        },
        {
          "name": "videoUrl",
          "description": "Details only: main product video link."
        },
        {
          "name": "category",
          "description": "Category id; the name is added with details."
        },
        {
          "name": "hasDetails",
          "type": "boolean",
          "description": "True when the row carries the detail page fields (skus, attributes, freight, rating)."
        },
        {
          "name": "scrapedAt",
          "description": "ISO time the row was collected."
        },
        {
          "name": "stated",
          "description": "Coverage rows: total results 1688 reports for the keyword."
        },
        {
          "name": "delivered",
          "description": "Coverage rows: how many products were delivered for the keyword."
        },
        {
          "name": "stopReason",
          "description": "Coverage rows: why the search stopped: limit, end, cap, filter, budget or error."
        },
        {
          "name": "shortfall",
          "description": "Coverage rows: true when the result may be incomplete: fewer delivered than reported with no cap or filter to explain it, or an error. shortfallReason says which."
        },
        {
          "name": "shortfallReason",
          "description": "Coverage rows: why shortfall is true. below_stated (fewer rows than the site states, no maximum or filter), error (ended by a failure). Null when shortfall is false."
        },
        {
          "name": "input",
          "description": "Coverage and error rows: the keyword or offer the row is about."
        },
        {
          "name": "code",
          "description": "Error rows: invalid_input, not_found, no_match, blocked or unavailable."
        },
        {
          "name": "message",
          "description": "Error rows: what went wrong, in plain words."
        }
      ],
      "sampleRows": [
        {
          "rowType": "product",
          "platform": "1688",
          "offerId": "866095761513",
          "url": "https://detail.1688.com/offer/866095761513.html",
          "title": "数据线保护套硅胶防折断充电线保护套咬线器防断裂神器",
          "keyword": "数据线",
          "rank": 2,
          "isAd": false,
          "currency": "CNY",
          "priceMin": 2.5,
          "priceMax": 2.6,
          "priceTiers": [
            {
              "minQty": 3,
              "maxQty": 4999,
              "price": 2.6
            },
            {
              "minQty": 5000,
              "maxQty": 99999,
              "price": 2.55
            }
          ],
          "moq": 3,
          "unit": "件",
          "soldCount": 100000,
          "soldText": "已售10万+件",
          "soldScope": "offer",
          "repurchaseRate": 17,
          "origin": {
            "province": "广东",
            "city": "东莞市"
          },
          "supplier": {
            "companyName": "东莞市元拓橡塑制品有限公司",
            "memberId": "b2b-22072902285003c713",
            "isFactory": true,
            "yearsOnPlatform": 7,
            "serviceScores": {
              "composite": 4,
              "consultation": 4,
              "logistics": 3.57,
              "dispute": 5,
              "returns": 3.67,
              "goods": 3.33
            },
            "repurchaseRate3m": 77,
            "shopUrl": "https://apetop.1688.com"
          },
          "hasDetails": false
        }
      ],
      "prices": {
        "events": {
          "product": {
            "usd": 0.0015,
            "title": "Product (search result)"
          },
          "product-detail": {
            "usd": 0.0035,
            "title": "Product with details"
          }
        },
        "primaryEvent": "product",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:43:51.000Z"
    },
    {
      "slug": "aliexpress-search",
      "family": "ecommerce",
      "title": "AliExpress Search Scraper: Products, Price, Orders, Rank",
      "description": "Find winning products for dropshipping, track how a keyword's prices and best sellers move, or feed a product research agent. Type keywords and get the AliExpress search results as one JSON row per product: title, price, original price and discount, units sold, rating, ship-from country, delivery estimate, ad flag and the position in the results.",
      "inputSchema": {
        "title": "AliExpress Search Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "searchTerms": {
            "title": "Search terms",
            "type": "array",
            "description": "Keywords to search AliExpress for, for example \"phone case\". Each term is searched on its own and every product keeps the term and its position in the results. Also read from keywords, searchQueries or queries.",
            "editor": "stringList",
            "prefill": [
              "phone case"
            ]
          },
          "maxResultsPerTerm": {
            "title": "Max results per term",
            "type": "integer",
            "description": "How many products to return per search term, for example 120. Default: 60. A results page holds about 60 products and AliExpress shows at most 60 pages, so 3,600 is the ceiling. Also read from maxItems, maxResults or maxProducts (other AliExpress actors' names; 0 = the ceiling).",
            "minimum": 1,
            "maximum": 3600,
            "prefill": 30
          },
          "sort": {
            "title": "Sort by",
            "type": "string",
            "description": "Best match (the site's default), most orders, or price, for example orders. Default: relevance. The other AliExpress actors' values default, price_asc and price_desc are accepted too, and rating is read as relevance (also under the name sortBy).",
            "enum": [
              "relevance",
              "orders",
              "priceAsc",
              "priceDesc",
              "default",
              "rating",
              "price_asc",
              "price_desc"
            ],
            "enumTitles": [
              "Best match",
              "Most orders",
              "Price, low to high",
              "Price, high to low",
              "Best match (legacy name)",
              "Best match (rating is not available)",
              "Price, low to high (legacy name)",
              "Price, high to low (legacy name)"
            ],
            "editor": "select",
            "prefill": "relevance"
          },
          "minPrice": {
            "title": "Minimum price",
            "type": "number",
            "description": "Only products priced at or above this amount (in the shop currency, US dollars). Filtered result pages hold fewer products, so a run may need more pages to reach the maximum. Example: 2. Also read from priceMin.",
            "editor": "number"
          },
          "maxPrice": {
            "title": "Maximum price",
            "type": "number",
            "description": "Only products priced at or below this amount (US dollars), for example 10. Also read from priceMax.",
            "editor": "number"
          },
          "shipFrom": {
            "title": "Ships from",
            "type": "string",
            "description": "Two-letter code of the warehouse country, for example CN or US. Leave empty for all. Also read from shipFromCountry.",
            "editor": "textfield"
          },
          "freeShipping": {
            "title": "Free shipping only",
            "type": "boolean",
            "description": "Only products that ship for free. Default: false. Also read from freeShippingOnly.",
            "prefill": false
          },
          "includeAds": {
            "title": "Include paid placements",
            "type": "boolean",
            "description": "Sponsored products are part of the search results. Each row has an isAd flag; turn this off to drop them (they still count towards the position).",
            "default": true
          },
          "includeValidationReport": {
            "title": "Add a market validation report per term",
            "type": "boolean",
            "description": "Default off. On: one extra `validation_report` row per term, charged as its own event ($0.02 per report), computed from that term's product rows: stated result count, price p10/median/p90 per currency, sold-count distribution, sales concentration in the top products, ad, Choice and free-shipping shares, rating bands, and 12-month Google Trends interest with its slope. Use 300+ results per term for a stable picture.",
            "default": false,
            "sectionCaption": "Validation report"
          },
          "trendsGeo": {
            "title": "Google Trends region",
            "type": "string",
            "description": "Example: `US`. Two-letter country code for the Trends part of the validation report; empty = worldwide. Ignored when the report is off.",
            "editor": "textfield",
            "default": ""
          },
          "includeErrorRows": {
            "title": "Add a row for terms that failed",
            "type": "boolean",
            "description": "Adds a free row for every term that returned nothing or could not be read, so a failure is never silent.",
            "default": true,
            "sectionCaption": "Output"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'product': one product (charged). 'validation_report': one market summary per search term, only with includeValidationReport (charged as validation-report). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "statedResults",
          "description": "validation_report rows: result count AliExpress states for the term across all pages, e.g. 48213; null when not stated."
        },
        {
          "name": "sampleSize",
          "description": "validation_report rows: product rows of this run the report is computed from (= delivered products for the term)."
        },
        {
          "name": "sort",
          "description": "validation_report rows: sort order of the sample: relevance, orders, priceAsc or priceDesc."
        },
        {
          "name": "priceBands",
          "description": "validation_report rows: per currency { currency, products, min, p10, median, p90, max } of the regular sale price, e.g. USD 2.10 / 4.99 / 12.50 (p10 / median / p90). New-user promo prices are never included."
        },
        {
          "name": "newUserPromoShare",
          "description": "validation_report rows: share (0-1) of the sample that showed AliExpress' new-user promo price (promoType 'new_user')."
        },
        {
          "name": "discountStats",
          "description": "validation_report rows: { shareDiscounted (0-1, share of products with a discount), medianPercent (median discount in %) }."
        },
        {
          "name": "soldStats",
          "description": "validation_report rows: units sold per product { productsWithSold, totalSoldMin, min, p10, median, p90, max, share100Plus, share1000Plus (0-1 of the sample) }; counts are lower bounds ('10,000+ sold' = 10000). Null when no product shows sales."
        },
        {
          "name": "salesConcentration",
          "description": "validation_report rows: share of the sample's units sold held by the top 1, 5 and 10 products (0-1) and the HHI (0-1; above 0.25 = a few listings own the demand). Search cards carry no seller id, so this is per product, not per seller."
        },
        {
          "name": "adShare",
          "description": "validation_report rows: share (0-1) of paid placements in the sample; null when includeAds was off."
        },
        {
          "name": "choiceShare",
          "description": "validation_report rows: share (0-1) of the sample showing the Choice badge."
        },
        {
          "name": "freeShippingShare",
          "description": "validation_report rows: share (0-1) of the sample showing free shipping."
        },
        {
          "name": "ratingStats",
          "description": "validation_report rows: { productsRated, ratedShare (0-1), mean, median, share45Plus, share40To45, shareBelow40 (0-1 of rated products) } on the 1-5 star scale. Null when no product is rated."
        },
        {
          "name": "shipFromMix",
          "description": "validation_report rows: warehouse countries [{ country, share (0-1) }], biggest first, over products that state one."
        },
        {
          "name": "deliveryMaxDaysMedian",
          "description": "validation_report rows: median of the slowest delivery estimate in days."
        },
        {
          "name": "trends",
          "description": "validation_report rows: 12-month Google Trends interest of the term { geo, timeRange, monthly [{ month 'YYYY-MM', value 0-100 }], average, slopePerMonth (index points per month), lastVsFirstQuarterPct, direction rising|flat|falling }; null unless trendsStatus is ok."
        },
        {
          "name": "trendsStatus",
          "description": "validation_report rows: ok, no_data (too few searches for Trends) or unavailable (Trends rate-limited the request; the rest of the report is complete)."
        },
        {
          "name": "generatedAt",
          "description": "validation_report rows: ISO timestamp of the report."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always 'aliexpress'."
        },
        {
          "name": "productId",
          "type": "string",
          "description": "AliExpress item id (the 1005... number in the product URL)."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Product title as listed."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Product page URL."
        },
        {
          "name": "price",
          "type": "number",
          "description": "Regular sale price in USD (lowest when the product has variants). On a new-user promo card it is the struck-through regular price of the listed variant; null only when such a card shows no struck-through price. The promo (see promoPrice) is never put here."
        },
        {
          "name": "priceMax",
          "description": "Highest variant price in USD; equals price when there is one price; null with price."
        },
        {
          "name": "currency",
          "type": "string",
          "description": "Currency code of the prices, normally USD."
        },
        {
          "name": "originalPrice",
          "type": "number",
          "description": "Struck-through list price in USD as the card shows it."
        },
        {
          "name": "discountPercent",
          "type": "number",
          "description": "Discount off originalPrice in percent. Null on new-user promo cards (the shown discount is the promo's)."
        },
        {
          "name": "promoPrice",
          "description": "AliExpress' new-user welcome price shown to first-time visitors, e.g. 1.09 (USD). Set only when promoType is set; price then holds the struck-through regular price."
        },
        {
          "name": "promoType",
          "description": "'new_user' when the card carries AliExpress' new-user / welcome-deal marker, else null."
        },
        {
          "name": "soldText",
          "type": "string",
          "description": "Units sold as shown on the site, e.g. '20K+ sold'."
        },
        {
          "name": "soldMin",
          "type": "number",
          "description": "Number parsed from soldText. A lower bound when soldText ends in '+'."
        },
        {
          "name": "soldCount",
          "type": "number",
          "description": "The site's exact unrounded sold counter, when the listing carries it."
        },
        {
          "name": "rating",
          "type": "number",
          "description": "Average star rating, 1 to 5."
        },
        {
          "name": "imageUrl",
          "description": "Main image URL."
        },
        {
          "name": "shipFrom",
          "type": "string",
          "description": "Warehouse country code (e.g. 'CN', 'US') when the listing states it."
        },
        {
          "name": "freeShipping",
          "description": "True when shipping to the US is free."
        },
        {
          "name": "deliveryText",
          "description": "Delivery estimate as shown, e.g. a date range."
        },
        {
          "name": "deliveryMinDays",
          "description": "Fastest estimated delivery in days."
        },
        {
          "name": "deliveryMaxDays",
          "description": "Slowest estimated delivery in days."
        },
        {
          "name": "isChoice",
          "description": "True when the listing carries AliExpress' Choice label."
        },
        {
          "name": "isAd",
          "type": "boolean",
          "description": "True when the listing is a paid placement."
        },
        {
          "name": "looseMatch",
          "description": "True when AliExpress found few or no real matches and listed loosely related products instead."
        },
        {
          "name": "searchTerm",
          "type": "string",
          "description": "The keyword that produced this row."
        },
        {
          "name": "searchRank",
          "type": "number",
          "description": "1-based position in the results for searchTerm, ads included."
        },
        {
          "name": "scrapedAt",
          "description": "ISO timestamp of when the row was collected."
        },
        {
          "name": "input",
          "description": "Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'."
        },
        {
          "name": "code",
          "description": "Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing)."
        },
        {
          "name": "message",
          "description": "Error rows: short human-readable explanation. Do not parse it, use code."
        }
      ],
      "sampleRows": [
        {
          "rowType": "product",
          "platform": "aliexpress",
          "productId": "1005007097199333",
          "title": "Wireless LED Night Lights Motion Sensor Lights Closet Kitchen",
          "url": "https://www.aliexpress.com/item/1005007097199333.html",
          "price": 0.33,
          "currency": "USD",
          "originalPrice": 5.36,
          "discountPercent": 93,
          "soldText": "100K+ sold",
          "soldMin": 100000,
          "soldCount": 241114,
          "rating": 4.9,
          "shipFrom": "CN",
          "isAd": false,
          "searchTerm": "lamp",
          "searchRank": 1
        }
      ],
      "prices": {
        "events": {
          "result": {
            "usd": 0.00125,
            "title": "Product"
          },
          "validation-report": {
            "usd": 0.02,
            "title": "Validation report"
          }
        },
        "primaryEvent": "result",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T00:03:22.000Z"
    },
    {
      "slug": "etsy-products",
      "family": "ecommerce",
      "title": "Etsy Scraper: Product Listings, Prices, Tags, Shop Sales",
      "description": "Research a niche before you open a shop, price your products against the market, or track the tags and best sellers of competing shops. Give it keywords, Etsy category links or listing URLs and it returns one JSON row per listing: price and sale price, favorites, the listing's own rating and review count, tags, materials, variations, shipping and delivery estimate, and the shop's sales count and rating. Review text is optional.",
      "inputSchema": {
        "title": "Etsy Products Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "searchTerms": {
            "title": "Search terms",
            "type": "array",
            "description": "Keywords to search Etsy for, for example \"ceramic mug\" or \"personalized necklace\". Each term is searched on its own; every listing keeps the term and its position in the results. Also read from queries, searchQueries, searchQuery, search or keywords (other Etsy actors' names).",
            "editor": "stringList",
            "prefill": [
              "ceramic mug"
            ]
          },
          "startUrls": {
            "title": "Etsy URLs or listing ids",
            "type": "array",
            "description": "Listing URLs or ids (e.g. https://www.etsy.com/listing/962939986 or 962939986) for full details, and search or category URLs (e.g. https://www.etsy.com/c/jewelry/necklaces) to read their results. Also read from listingUrls, urls or listingIds; objects with a url field work too.",
            "editor": "stringList"
          },
          "maxResultsPerSearch": {
            "title": "Max listings per search",
            "type": "integer",
            "description": "Listings per search term or search/category URL, e.g. 50 (the default). Each page holds 40 to 64 results; Etsy pages up to about 250 pages deep. Also read from maxResults, maxResultsPerTerm, maxListings or maxPages (x 36).",
            "minimum": 1,
            "maximum": 10000,
            "prefill": 20
          },
          "maxItems": {
            "title": "Max rows in total",
            "type": "integer",
            "description": "Optional cap on charged rows (listings plus reviews) for the whole run, e.g. 500. Empty means no cap beyond the per-search limit.",
            "minimum": 1
          },
          "sortBy": {
            "title": "Sort by",
            "type": "string",
            "description": "Order of search results: relevance (Etsy's default), newest, price_asc, price_desc or top_reviews, for example newest. Also read from sort, sortOrder or sortOn (other Etsy actors' names and values).",
            "enum": [
              "relevance",
              "newest",
              "price_asc",
              "price_desc",
              "top_reviews"
            ],
            "enumTitles": [
              "Relevance",
              "Newest",
              "Price, low to high",
              "Price, high to low",
              "Top customer reviews"
            ],
            "editor": "select"
          },
          "minPrice": {
            "title": "Minimum price",
            "type": "number",
            "description": "Only listings priced at or above this amount, in the shopper currency (US dollars for the default US storefront), e.g. 20.",
            "editor": "number"
          },
          "maxPrice": {
            "title": "Maximum price",
            "type": "number",
            "description": "Only listings priced at or below this amount, in US dollars, e.g. 60.",
            "editor": "number"
          },
          "shipTo": {
            "title": "Ships to",
            "type": "string",
            "description": "Two-letter country code the items must ship to, e.g. US, GB or DE. Empty means any.",
            "editor": "textfield"
          },
          "freeShipping": {
            "title": "Free shipping only",
            "type": "boolean",
            "description": "Only listings that ship for free (to the US storefront)."
          },
          "onSale": {
            "title": "On sale only",
            "type": "boolean",
            "description": "Only listings with a running discount (salePrice and discountPercent are filled)."
          },
          "includeAds": {
            "title": "Include paid placements",
            "type": "boolean",
            "description": "Also return the sponsored listings shown among the results (isAd: true, no rank). Off by default; they do not count towards the per-search limit.",
            "default": false
          },
          "maxReviewsPerListing": {
            "title": "Reviews per listing",
            "type": "integer",
            "description": "Newest reviews to return per listing as separate review rows (text, rating, date, shop reply; no reviewer names), e.g. 20. 0 = none (the default); the rating summary is always on the listing.",
            "minimum": 0,
            "maximum": 5000
          },
          "reviewSort": {
            "title": "Review order",
            "type": "string",
            "description": "Which reviews come first when reviews are requested: newest, relevance, highest or lowest rating.",
            "enum": [
              "newest",
              "relevance",
              "highest",
              "lowest"
            ],
            "enumTitles": [
              "Newest",
              "Most relevant",
              "Highest rated",
              "Lowest rated"
            ],
            "default": "newest",
            "editor": "select"
          },
          "description": {
            "title": "Description",
            "type": "string",
            "description": "Listing description in the output: short (first 300 characters, the default), full, or none. Emails and phone numbers are removed.",
            "enum": [
              "short",
              "full",
              "none"
            ],
            "enumTitles": [
              "Short (300 characters)",
              "Full text",
              "Leave out"
            ],
            "editor": "select"
          },
          "includeShipping": {
            "title": "Shipping details",
            "type": "boolean",
            "description": "Adds the shipping block per listing: ships from, free or cost, delivery estimate to the US and the returns policy.",
            "default": true
          },
          "includeErrorRows": {
            "title": "Add a row for inputs that failed",
            "type": "boolean",
            "description": "Adds a free row for every input that returned nothing or could not be read, so a failure is never silent.",
            "default": true
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. 'listing': one Etsy listing (charged). 'review': one listing review, only when reviews are requested (charged). 'error': an input could not be read; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always 'etsy'."
        },
        {
          "name": "listingId",
          "type": "string",
          "description": "Etsy listing id (the number in the listing URL). Listing and review rows."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Listing page URL."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Listing title as the seller wrote it."
        },
        {
          "name": "description",
          "description": "Listing description (first 300 characters by default, full or none on request). Emails and phone numbers removed."
        },
        {
          "name": "price",
          "type": "number",
          "description": "Price before any sale, in 'currency' (the seller's currency). Lowest variation price when prices vary."
        },
        {
          "name": "currency",
          "type": "string",
          "description": "Currency code of price and salePrice, e.g. USD, EUR, GBP."
        },
        {
          "name": "salePrice",
          "type": "number",
          "description": "Price during a running sale, in 'currency'; null when not on sale."
        },
        {
          "name": "discountPercent",
          "type": "number",
          "description": "Sale discount in percent; null when not on sale."
        },
        {
          "name": "onSale",
          "type": "boolean",
          "description": "True when a sale discount is running."
        },
        {
          "name": "priceUsd",
          "description": "The price converted by Etsy to US dollars."
        },
        {
          "name": "quantity",
          "description": "Items in stock as the listing states."
        },
        {
          "name": "isAvailable",
          "description": "True when the listing can be bought now."
        },
        {
          "name": "isSoldOut",
          "description": "True when the listing is sold out."
        },
        {
          "name": "favorites",
          "type": "number",
          "description": "How many shoppers saved the listing as a favorite."
        },
        {
          "name": "rating",
          "type": "number",
          "description": "Average star rating of this listing (not the shop), 1 to 5; null without reviews."
        },
        {
          "name": "reviewCount",
          "type": "number",
          "description": "Number of reviews of this listing."
        },
        {
          "name": "ratingCounts",
          "description": "Reviews of this listing per star, e.g. {'5': 236, '4': 6, '3': 2, '2': 0, '1': 1}."
        },
        {
          "name": "tags",
          "type": "array",
          "description": "The seller's search tags (up to 13)."
        },
        {
          "name": "materials",
          "type": "array",
          "description": "Materials the seller lists."
        },
        {
          "name": "category",
          "type": "string",
          "description": "Etsy category of the listing, e.g. Wallets."
        },
        {
          "name": "categoryPath",
          "description": "Full category path, e.g. Bags and purses > Wallets and money clips > Wallets."
        },
        {
          "name": "taxonomyId",
          "description": "Etsy taxonomy id of the category."
        },
        {
          "name": "variations",
          "type": "array",
          "description": "Buyer options, e.g. [{name: 'Primary color', options: [{value: 'Tan', price: 64, available: true}]}]. Option price is null when the option does not change the price."
        },
        {
          "name": "hasVariationPricing",
          "description": "True when the price depends on the chosen option."
        },
        {
          "name": "images",
          "description": "Full-size image URLs, main image first."
        },
        {
          "name": "whenMade",
          "type": "string",
          "description": "When the item was made as Etsy states it, e.g. 'made_to_order', '2020_2026', 'before_2006'."
        },
        {
          "name": "isDigital",
          "description": "True for digital downloads (no shipping)."
        },
        {
          "name": "isCustomizable",
          "description": "True when the item can be personalized."
        },
        {
          "name": "isMadeToOrder",
          "description": "True when the item is made after the order."
        },
        {
          "name": "isBestseller",
          "description": "True when the listing carries Etsy's Bestseller badge."
        },
        {
          "name": "shipsFromCountry",
          "type": "string",
          "description": "Country code the item ships from, e.g. US, TR."
        },
        {
          "name": "shipping",
          "type": "object",
          "description": "Shipping as Etsy shows it to a US shopper: {shipsFrom, freeShipping, cost, costText, deliveryEstimate, deliverTo, returns}. Null for digital items or when 'includeShipping' is off."
        },
        {
          "name": "shopSection",
          "description": "The shop's own section the listing is in."
        },
        {
          "name": "shop",
          "type": "object",
          "description": "The shop as a business: {shopId, shopName, url, sales, rating, reviewCount, location, country, activeListings, favorites, openedAt, isStarSeller}. No owner names."
        },
        {
          "name": "createdAt",
          "description": "Listing date Etsy keeps (ISO). A renewal resets it."
        },
        {
          "name": "updatedAt",
          "description": "Last change of the listing (ISO)."
        },
        {
          "name": "isAd",
          "type": "boolean",
          "description": "True for a paid placement (only with includeAds; no searchRank)."
        },
        {
          "name": "searchInput",
          "type": "string",
          "description": "The search term or URL that found the listing; null for a listing URL input."
        },
        {
          "name": "searchRank",
          "type": "number",
          "description": "1-based organic position in the results of searchInput."
        },
        {
          "name": "reviewId",
          "description": "Review rows: Etsy's transaction id of the reviewed purchase, unique per review."
        },
        {
          "name": "text",
          "description": "Review rows: the review text with personal names, emails and phone numbers removed; null for a rating without text."
        },
        {
          "name": "date",
          "description": "Review rows: review date (YYYY-MM-DD)."
        },
        {
          "name": "sellerResponse",
          "description": "Review rows: the shop's public reply, if any."
        },
        {
          "name": "hasPhoto",
          "description": "Review rows: true when the buyer attached a photo (not exported)."
        },
        {
          "name": "input",
          "description": "Error rows: the input (term, URL or id) that failed."
        },
        {
          "name": "code",
          "description": "Error rows: invalid_input, not_found, blocked, unavailable, filter_unsupported or no_match."
        },
        {
          "name": "message",
          "description": "Error rows: what went wrong, in plain words."
        },
        {
          "name": "scrapedAt",
          "description": "When the row was collected (ISO)."
        }
      ],
      "sampleRows": [
        {
          "rowType": "listing",
          "platform": "etsy",
          "listingId": "962939986",
          "title": "Leather Envelope Wallet for Women, Personalized Mini Card Wallet, Full Grain Crazy Horse",
          "url": "https://www.etsy.com/listing/962939986",
          "price": 64,
          "currency": "USD",
          "salePrice": 57.6,
          "discountPercent": 10,
          "onSale": true,
          "favorites": 4979,
          "rating": 4.95,
          "reviewCount": 245,
          "tags": [
            "Envelope Wallet",
            "Women's Leather"
          ],
          "materials": [
            "Full Grain Crazy Horse Leather"
          ],
          "category": "Wallets",
          "variations": [
            {
              "name": "Primary color",
              "options": [
                {
                  "value": "Tan",
                  "price": null,
                  "available": true
                }
              ]
            }
          ],
          "whenMade": "2020_2026",
          "shipsFromCountry": "TR",
          "shipping": {
            "shipsFrom": "Türkiye",
            "freeShipping": true,
            "deliveryEstimate": "Oct 26-Nov 2",
            "deliverTo": "United States",
            "returns": "Returns & exchanges accepted within 30 days."
          },
          "shop": {
            "shopId": "28165607",
            "shopName": "YukonLeatherGoods",
            "sales": 6583,
            "rating": 4.92,
            "reviewCount": 1412,
            "location": "Dover, Delaware",
            "isStarSeller": true
          },
          "searchInput": "leather wallet",
          "searchRank": 3,
          "isAd": false
        }
      ],
      "prices": {
        "events": {
          "listing": {
            "usd": 0.0035,
            "title": "Listing"
          },
          "review": {
            "usd": 0.001,
            "title": "Review"
          }
        },
        "primaryEvent": "listing",
        "startUsdPerRun": 0.002,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-02T04:18:10.000Z"
    },
    {
      "slug": "youtube-transcripts",
      "family": "video",
      "title": "YouTube Transcript & Subtitles Scraper: Text, SRT, VTT",
      "description": "Give it YouTube video, Shorts, channel or playlist links (or plain video IDs) and it returns each transcript (subtitles, captions) as JSON: full text, timed segments, language, whether it was auto-generated, every caption language the video has, and the video's title, channel, length, publish date and views.",
      "inputSchema": {
        "title": "YouTube Transcripts Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "urls": {
            "title": "YouTube URLs or video IDs",
            "type": "array",
            "description": "Example: https://www.youtube.com/watch?v=dQw4w9WgXcQ, https://youtu.be/dQw4w9WgXcQ, a Shorts URL, a bare video ID (dQw4w9WgXcQ), a channel (https://www.youtube.com/@kurzgesagt, add /shorts or /streams for those tabs) or a playlist URL. Channels and playlists give up to maxVideosPerSource videos each, channels newest first.",
            "editor": "stringList",
            "prefill": [
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ]
          },
          "languages": {
            "title": "Preferred caption languages",
            "type": "array",
            "description": "ISO language codes in order of preference, e.g. [\"en\", \"de\"]; en also matches en-US and en-GB. The first one the video has is used. Empty or none available: the original spoken language (languageMatched is false when none matched).",
            "editor": "stringList",
            "prefill": []
          },
          "captionType": {
            "title": "Caption type",
            "type": "string",
            "description": "Values: any (uploaded captions first, else auto-generated), manual (uploaded by the creator only), auto (YouTube speech recognition only). Videos without that type give a free error row. Default: any.",
            "enum": [
              "any",
              "manual",
              "auto"
            ],
            "enumTitles": [
              "Any (uploaded first, then auto-generated)",
              "Uploaded by the creator only",
              "Auto-generated only"
            ],
            "editor": "select",
            "prefill": "any"
          },
          "translateTo": {
            "title": "Translate to",
            "type": "string",
            "description": "ISO language code, e.g. es. A native track in that language is used when the video has one (translationStatus original). Otherwise YouTube's own machine translation is asked for; YouTube currently refuses it for anonymous clients, so you get the original with translationStatus unavailable.",
            "editor": "textfield"
          },
          "maxVideosPerSource": {
            "title": "Max videos per channel or playlist",
            "type": "integer",
            "description": "Transcripts per channel or playlist URL, 1 to 5,000 (default 50). Videos without captions or outside the date range do not count. Direct video URLs are not limited by this.",
            "minimum": 1,
            "maximum": 5000,
            "prefill": 10
          },
          "publishedAfter": {
            "title": "Published on or after",
            "type": "string",
            "description": "Date as YYYY-MM-DD, e.g. 2026-01-01. Videos published earlier are skipped for free; on a channel the listing stops at the first older videos.",
            "editor": "datepicker"
          },
          "publishedBefore": {
            "title": "Published on or before",
            "type": "string",
            "description": "Date as YYYY-MM-DD, e.g. 2026-06-30. Videos published later are skipped for free.",
            "editor": "datepicker"
          },
          "includeSegments": {
            "title": "Include timed segments",
            "type": "boolean",
            "description": "Adds segments: one entry per caption line with start and duration in seconds and its text. The joined transcript text is always included.",
            "default": true
          },
          "subtitleFormat": {
            "title": "Subtitle file text",
            "type": "string",
            "description": "Values: none, srt, vtt. srt or vtt adds the captions as a ready subtitle file in the srt or vtt field. Default: none.",
            "enum": [
              "none",
              "srt",
              "vtt"
            ],
            "enumTitles": [
              "None",
              "SRT",
              "WebVTT"
            ],
            "editor": "select",
            "prefill": "none"
          },
          "description": {
            "title": "Video description",
            "type": "string",
            "description": "Values: short (about 300 characters, default), full, none. Emails and phone numbers are removed.",
            "enum": [
              "short",
              "full",
              "none"
            ],
            "enumTitles": [
              "Short",
              "Full",
              "None"
            ],
            "default": "short",
            "editor": "select"
          },
          "maxResults": {
            "title": "Max transcripts in total",
            "type": "integer",
            "description": "Stops the run after this many transcripts across all URLs, 1 to 1,000,000. A cost cap; leave empty for no limit.",
            "minimum": 1,
            "maximum": 1000000
          },
          "includeErrorRows": {
            "title": "Add a row for videos without a transcript",
            "type": "boolean",
            "description": "Adds a free row (rowType error, with a code and message) for every video or URL that gave no transcript: no captions, private, age-restricted, not found. On by default.",
            "default": true
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'transcript': one video's transcript (charged). 'error': a video or URL gave no transcript; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always 'youtube'."
        },
        {
          "name": "videoId",
          "type": "string",
          "description": "YouTube video id, 11 characters, e.g. dQw4w9WgXcQ."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Watch page link of the video (Shorts included)."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Video title."
        },
        {
          "name": "channelName",
          "type": "string",
          "description": "Name of the channel that published the video (the publisher, not a viewer)."
        },
        {
          "name": "channelId",
          "type": "string",
          "description": "Channel id, starts with UC, e.g. UCuAXFkgsw1L7xaCfnd5JJOw."
        },
        {
          "name": "channelHandle",
          "type": "string",
          "description": "Channel handle with the @, e.g. @RickAstleyYT. Null if YouTube did not return it."
        },
        {
          "name": "channelUrl",
          "type": "string",
          "description": "Link to the channel page built from channelId."
        },
        {
          "name": "durationSeconds",
          "type": "number",
          "description": "Video length in seconds."
        },
        {
          "name": "publishDate",
          "type": "string",
          "description": "Publish date and time as YouTube gives it, ISO 8601 with offset, e.g. 2009-10-24T23:57:33-07:00."
        },
        {
          "name": "viewCount",
          "type": "number",
          "description": "Views at the time of the run."
        },
        {
          "name": "likeCount",
          "type": "number",
          "description": "Likes at the time of the run; null when hidden."
        },
        {
          "name": "category",
          "type": "string",
          "description": "YouTube category, e.g. Music, Education, News & Politics."
        },
        {
          "name": "isLiveContent",
          "type": "boolean",
          "description": "True for live streams and recorded live streams."
        },
        {
          "name": "keywords",
          "type": "array",
          "description": "Tags the creator set on the video (may be empty)."
        },
        {
          "name": "description",
          "type": "string",
          "description": "Video description, short (about 300 characters) or full per input; emails and phone numbers are removed."
        },
        {
          "name": "language",
          "type": "string",
          "description": "Language code of the returned transcript, e.g. en, de-DE, pt-BR."
        },
        {
          "name": "languageName",
          "type": "string",
          "description": "English name of that language, e.g. German (Germany)."
        },
        {
          "name": "isAutoGenerated",
          "type": "boolean",
          "description": "True when the transcript is YouTube's speech recognition (no punctuation, may misspell names); false when the creator uploaded it."
        },
        {
          "name": "languageMatched",
          "type": "boolean",
          "description": "False when none of the requested languages existed and the original-language track was returned instead."
        },
        {
          "name": "translationStatus",
          "type": "string",
          "description": "Values: 'not_requested', 'original' (the video has a native track in translateTo), 'translated' (YouTube's own machine translation), 'unavailable' (YouTube refused it; the original is returned)."
        },
        {
          "name": "translatedFrom",
          "type": "null",
          "description": "Source language code when translationStatus is 'translated', else null."
        },
        {
          "name": "availableLanguages",
          "type": "array",
          "description": "Every caption track of the video: code, name, isAutoGenerated. Use it to request another language."
        },
        {
          "name": "transcript",
          "type": "string",
          "description": "The whole transcript as one text: caption lines joined with spaces. Emails and phone numbers are removed."
        },
        {
          "name": "segmentCount",
          "type": "number",
          "description": "Number of caption lines."
        },
        {
          "name": "wordCount",
          "type": "number",
          "description": "Words in transcript, for chunking and token estimates."
        },
        {
          "name": "segments",
          "type": "array",
          "description": "Caption lines in order: start (seconds from the start), duration (seconds), text. Present when includeSegments is on."
        },
        {
          "name": "srt",
          "description": "The captions as an SRT subtitle file, when subtitleFormat is srt."
        },
        {
          "name": "vtt",
          "description": "The captions as a WebVTT subtitle file, when subtitleFormat is vtt."
        },
        {
          "name": "source",
          "type": "null",
          "description": "The channel or playlist URL this video came from; null for videos given directly."
        },
        {
          "name": "scrapedAt",
          "type": "string",
          "description": "When the row was read, ISO 8601."
        },
        {
          "name": "input",
          "description": "Error rows: the URL or id as given."
        },
        {
          "name": "code",
          "description": "Error rows: not_found (private, removed, unknown channel), no_match (no captions, or none of the requested type), unavailable (age-restricted, members-only, upcoming, temporary error), blocked (rate limited, retry later), invalid_input."
        },
        {
          "name": "message",
          "description": "Error rows: what happened, in plain English."
        }
      ],
      "sampleRows": [
        {
          "rowType": "transcript",
          "platform": "youtube",
          "videoId": "dQw4w9WgXcQ",
          "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
          "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
          "channelName": "Rick Astley",
          "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
          "channelHandle": "@RickAstleyYT",
          "channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
          "durationSeconds": 213,
          "publishDate": "2009-10-24T23:57:33-07:00",
          "viewCount": 1829055386,
          "likeCount": 19491653,
          "category": "Music",
          "isLiveContent": false,
          "keywords": [
            "rick astley",
            "Never Gonna Give You Up"
          ],
          "description": "The official video for “Never Gonna Give You Up” by Rick Astley. Never: The Autobiography OUT NOW!...",
          "language": "en",
          "languageName": "English",
          "isAutoGenerated": false,
          "languageMatched": true,
          "translationStatus": "not_requested",
          "translatedFrom": null,
          "availableLanguages": [
            {
              "code": "en",
              "name": "English",
              "isAutoGenerated": false
            },
            {
              "code": "en",
              "name": "English",
              "isAutoGenerated": true
            }
          ],
          "transcript": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ...",
          "segmentCount": 61,
          "wordCount": 487,
          "segments": [
            {
              "start": 1.36,
              "duration": 1.68,
              "text": "[♪♪♪]"
            },
            {
              "start": 18.64,
              "duration": 3.24,
              "text": "♪ We're no strangers to love ♪"
            }
          ],
          "source": null,
          "scrapedAt": "2026-10-22T08:00:00.000Z"
        }
      ],
      "prices": {
        "events": {
          "transcript": {
            "usd": 0.0015,
            "title": "Transcript"
          }
        },
        "primaryEvent": "transcript",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-03T09:32:16.000Z"
    },
    {
      "slug": "youtube-videos",
      "family": "video",
      "title": "YouTube Scraper: Videos, Shorts, Channels, Search",
      "description": "Give it YouTube channels, playlists, search terms, hashtags or video links and it returns one JSON row per video or Short: title, channel, exact publish date, duration, views, likes, category, Shorts and live flags, tags, hashtags, description, thumbnail and caption languages. Shorts also get comment count and sound.",
      "inputSchema": {
        "title": "YouTube Videos Scraper input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "urls": {
            "title": "Channel, playlist, video or hashtag URLs",
            "type": "array",
            "description": "Example: https://www.youtube.com/@kurzgesagt (add /shorts or /streams for those tabs), @handle, a channel id (UC...), a playlist URL (https://www.youtube.com/playlist?list=...), a video or Shorts URL, a hashtag page (https://www.youtube.com/hashtag/cooking, add /shorts for Shorts only), a bare video id (dQw4w9WgXcQ) or a search page URL (https://www.youtube.com/results?search_query=lofi). Channels give up to maxResults videos each, newest first by default.",
            "editor": "stringList",
            "prefill": [
              "https://www.youtube.com/@kurzgesagt"
            ]
          },
          "searchQueries": {
            "title": "Search terms",
            "type": "array",
            "description": "YouTube search terms, e.g. [\"web scraping tutorial\", \"iphone 17 review\"]. Each term gives up to maxResults videos in YouTube's search order (position is the rank). Can be combined with urls.",
            "editor": "stringList",
            "prefill": []
          },
          "hashtags": {
            "title": "Hashtags",
            "type": "array",
            "description": "YouTube hashtags, e.g. [\"#cooking\", \"dancechallenge\"] (letters, digits and _ only). Each gives up to maxResults videos from the hashtag page; with contentType shorts only its Shorts. Can be combined with urls and searchQueries.",
            "editor": "stringList",
            "prefill": []
          },
          "contentType": {
            "title": "Videos or Shorts",
            "type": "string",
            "description": "Values: videos (default), shorts. With shorts, search terms return Shorts only and hashtags read their Shorts tab. Channels: use maxResultsShorts or a /shorts channel URL. Full Shorts rows add commentCount, soundTitle and soundArtist and are charged as the short event.",
            "enum": [
              "videos",
              "shorts"
            ],
            "enumTitles": [
              "Videos",
              "Shorts only"
            ],
            "prefill": "videos",
            "editor": "select"
          },
          "maxResults": {
            "title": "Max videos per channel, playlist or search term",
            "type": "integer",
            "description": "Videos per channel videos tab, playlist or search term, up to 10,000 (default 50). Videos outside the date range, private or members-only videos do not count. Direct video URLs are not limited by this.",
            "minimum": 0,
            "maximum": 10000,
            "prefill": 10
          },
          "maxResultsShorts": {
            "title": "Max Shorts per channel",
            "type": "integer",
            "description": "Also read the channel's Shorts tab, up to this many Shorts per channel (0 = skip the tab, default). A /shorts channel URL reads only that tab.",
            "minimum": 0,
            "maximum": 10000,
            "default": 0
          },
          "maxResultStreams": {
            "title": "Max live streams per channel",
            "type": "integer",
            "description": "Also read the channel's Live tab (past, running and scheduled streams), up to this many per channel (0 = skip the tab, default).",
            "minimum": 0,
            "maximum": 10000,
            "default": 0
          },
          "maxItems": {
            "title": "Max videos in total",
            "type": "integer",
            "description": "Stops the run after this many videos across all inputs, 1 to 1,000,000. A cost cap; leave empty for no limit.",
            "minimum": 1,
            "maximum": 1000000
          },
          "publishedAfter": {
            "title": "Published on or after",
            "type": "string",
            "description": "Date as YYYY-MM-DD (e.g. 2026-01-01) or a relative age such as \"30 days\", \"2 weeks\", \"1 year\". Older videos are skipped for free; a channel listed newest first stops at the first older videos.",
            "editor": "datepicker",
            "dateType": "absoluteOrRelative"
          },
          "publishedBefore": {
            "title": "Published on or before",
            "type": "string",
            "description": "Date as YYYY-MM-DD (e.g. 2026-06-30) or a relative age such as \"7 days\". Newer videos are skipped for free.",
            "editor": "datepicker",
            "dateType": "absoluteOrRelative"
          },
          "sortVideosBy": {
            "title": "Channel sort order",
            "type": "string",
            "description": "Values: NEWEST (default), POPULAR (most viewed first), OLDEST. Applies to channel tabs. The date-range early stop works with NEWEST and OLDEST.",
            "enum": [
              "NEWEST",
              "POPULAR",
              "OLDEST"
            ],
            "enumTitles": [
              "Newest first",
              "Most popular first",
              "Oldest first"
            ],
            "editor": "select"
          },
          "listingOnly": {
            "title": "Fast mode: listing data only",
            "type": "boolean",
            "description": "Skips the per-video details request and returns what the channel, playlist or search listing shows: title, rounded views, duration, approximate date (publishDateIsApprox true), channel. No likes, category, description or caption languages. Charged at the lower video-listing price. Off by default."
          },
          "includeCaptionLanguages": {
            "title": "Include caption languages",
            "type": "boolean",
            "description": "Adds captionLanguages (every caption track: code, name, auto-generated flag) and hasCaptions. One more small request per video. On by default; ignored in fast mode.",
            "default": true
          },
          "includeCommentCount": {
            "title": "Include comment count for regular videos",
            "type": "boolean",
            "description": "Adds commentCount to regular videos (one more small request each). Shorts always get it. Ignored in fast mode. Off by default.",
            "default": false
          },
          "description": {
            "title": "Video description",
            "type": "string",
            "description": "Values: full (default), short (about 300 characters), none. Emails and phone numbers are removed.",
            "enum": [
              "full",
              "short",
              "none"
            ],
            "enumTitles": [
              "Full",
              "Short",
              "None"
            ],
            "default": "full",
            "editor": "select"
          },
          "sortingOrder": {
            "title": "Search sort order",
            "type": "string",
            "description": "Values: relevance (default), rating, date (upload date), views. Applies to searchQueries and search URLs.",
            "enum": [
              "relevance",
              "rating",
              "date",
              "views"
            ],
            "enumTitles": [
              "Relevance",
              "Rating",
              "Upload date",
              "View count"
            ],
            "editor": "select",
            "sectionCaption": "Search filters"
          },
          "searchCountry": {
            "title": "Search country",
            "type": "string",
            "description": "Two-letter country code YouTube ranks search results for, e.g. DE or IN (default US). Applies to searchQueries and search URLs; titles and dates stay as published.",
            "editor": "textfield"
          },
          "dateFilter": {
            "title": "Search upload date",
            "type": "string",
            "description": "Values: hour, today, week, month, year. YouTube's own upload-date filter for searches; it is loose, so combine it with publishedAfter for an exact range.",
            "enum": [
              "hour",
              "today",
              "week",
              "month",
              "year"
            ],
            "enumTitles": [
              "Last hour",
              "Today",
              "This week",
              "This month",
              "This year"
            ],
            "editor": "select"
          },
          "lengthFilter": {
            "title": "Search video length",
            "type": "string",
            "description": "Values: under4 (under 4 minutes), between420 (4 to 20 minutes), plus20 (over 20 minutes).",
            "enum": [
              "under4",
              "between420",
              "plus20"
            ],
            "enumTitles": [
              "Under 4 minutes",
              "4 to 20 minutes",
              "Over 20 minutes"
            ],
            "editor": "select"
          },
          "isHD": {
            "title": "Search: HD only",
            "type": "boolean",
            "description": "Search results in HD only (YouTube's HD feature filter)."
          },
          "hasSubtitles": {
            "title": "Search: with subtitles only",
            "type": "boolean",
            "description": "Search results that have subtitles or closed captions only."
          },
          "hasCC": {
            "title": "Search: Creative Commons only",
            "type": "boolean",
            "description": "Search results under a Creative Commons licence only."
          },
          "isLive": {
            "title": "Search: live now only",
            "type": "boolean",
            "description": "Search results that are live streams only."
          },
          "is4K": {
            "title": "Search: 4K only",
            "type": "boolean",
            "description": "Search results in 4K only (YouTube's 4K feature filter)."
          },
          "includeErrorRows": {
            "title": "Add a row for inputs and videos without data",
            "type": "boolean",
            "description": "Adds a free row (rowType error, with a code and message) for every URL, private, age-restricted, members-only or missing video that gave no data. On by default.",
            "default": true,
            "sectionCaption": "Advanced"
          }
        }
      },
      "outputFields": [
        {
          "name": "rowType",
          "type": "string",
          "description": "Kind of row. Filter on this first. Values: 'video': one video with its metadata (charged). 'error': a URL or video gave no data; see code and message (free)."
        },
        {
          "name": "platform",
          "type": "string",
          "description": "Always 'youtube'."
        },
        {
          "name": "videoId",
          "type": "string",
          "description": "YouTube video id, 11 characters, e.g. dQw4w9WgXcQ."
        },
        {
          "name": "url",
          "type": "string",
          "description": "Watch page link of the video (Shorts included)."
        },
        {
          "name": "title",
          "type": "string",
          "description": "Video title."
        },
        {
          "name": "channelName",
          "type": "string",
          "description": "Name of the channel that published the video (the publisher, not a viewer)."
        },
        {
          "name": "channelId",
          "type": "string",
          "description": "Channel id, starts with UC, e.g. UCuAXFkgsw1L7xaCfnd5JJOw."
        },
        {
          "name": "channelHandle",
          "type": "string",
          "description": "Channel handle with the @, e.g. @RickAstleyYT. Null if YouTube did not return it."
        },
        {
          "name": "channelUrl",
          "type": "string",
          "description": "Link to the channel page built from channelId."
        },
        {
          "name": "publishDate",
          "type": "string",
          "description": "Publish date. Full rows: ISO 8601 with YouTube's offset, e.g. 2009-10-24T23:57:33-07:00. Fast-mode rows: YYYY-MM-DD estimated from the relative date (publishDateIsApprox true)."
        },
        {
          "name": "publishDateIsApprox",
          "type": "boolean",
          "description": "True when publishDate was estimated from a relative date such as '3 months ago' (fast mode); false for exact dates."
        },
        {
          "name": "publishedText",
          "type": "null",
          "description": "Relative date as the listing shows it, e.g. '3 days ago', 'Streamed 2 weeks ago'. Null for direct video URLs."
        },
        {
          "name": "durationSeconds",
          "type": "number",
          "description": "Video length in seconds. Null for running live streams and some Shorts in fast mode."
        },
        {
          "name": "viewCount",
          "type": "number",
          "description": "Views at the time of the run. Exact on full rows, rounded (3.5M) on fast-mode rows."
        },
        {
          "name": "likeCount",
          "type": "number",
          "description": "Likes at the time of the run; null when hidden or in fast mode."
        },
        {
          "name": "commentCount",
          "type": "null",
          "description": "Comments at the time of the run, exact. Shorts in full mode always; regular videos when includeCommentCount is on; null otherwise. 0 when comments are off."
        },
        {
          "name": "category",
          "type": "string",
          "description": "YouTube category, e.g. Music, Education, News & Politics. Null in fast mode."
        },
        {
          "name": "isShort",
          "type": "boolean",
          "description": "True for YouTube Shorts. Null when unknown (fast mode outside the Shorts tab and search)."
        },
        {
          "name": "isLive",
          "type": "boolean",
          "description": "True while the video is a live stream running now."
        },
        {
          "name": "isLiveContent",
          "type": "boolean",
          "description": "True for live streams and recorded live streams. Null in fast mode."
        },
        {
          "name": "isUpcoming",
          "type": "boolean",
          "description": "True for a scheduled premiere or live stream that has not started."
        },
        {
          "name": "isUnlisted",
          "type": "boolean",
          "description": "True for unlisted videos (reachable by link only). Null in fast mode."
        },
        {
          "name": "isFamilySafe",
          "type": "boolean",
          "description": "YouTube's family-safe flag. Null in fast mode."
        },
        {
          "name": "keywords",
          "type": "array",
          "description": "Tags the creator set on the video (may be empty). Null in fast mode."
        },
        {
          "name": "hashtags",
          "type": "array",
          "description": "#hashtags in the title and description, lower-cased, without the #. Null in fast mode."
        },
        {
          "name": "soundTitle",
          "type": "null",
          "description": "Shorts: the sound or song the Short uses, as YouTube's sound link shows it (e.g. a song title or 'Original sound'). Null for regular videos, Shorts without a sound link and fast mode."
        },
        {
          "name": "soundArtist",
          "type": "null",
          "description": "Shorts: the artist or channel credited for the sound (the part after the dot on the sound link). Null when not shown."
        },
        {
          "name": "description",
          "type": "string",
          "description": "Video description, full or short per input; emails and phone numbers are removed. Null in fast mode."
        },
        {
          "name": "thumbnailUrl",
          "type": "string",
          "description": "Thumbnail image, 480x360 (hqdefault), exists for every video."
        },
        {
          "name": "thumbnailMaxUrl",
          "type": "string",
          "description": "Largest thumbnail (maxresdefault, 1280x720 or more) when the upload has one; null otherwise or in fast mode."
        },
        {
          "name": "captionLanguages",
          "type": "array",
          "description": "Every caption track of the video: code (e.g. en, de-DE), name, isAutoGenerated. Empty list: no captions. Null when not requested, in fast mode, or YouTube did not answer."
        },
        {
          "name": "hasCaptions",
          "type": "boolean",
          "description": "True when the video has at least one caption track; null when captionLanguages is null."
        },
        {
          "name": "detailLevel",
          "type": "string",
          "description": "'full': read from the video's player data (exact date, likes, category). 'listing': only what the channel, playlist or search listing shows (fast mode)."
        },
        {
          "name": "source",
          "type": "null",
          "description": "The channel tab, playlist URL or search term this video came from; null for videos given directly."
        },
        {
          "name": "sourceType",
          "type": "string",
          "description": "Values: 'video' (given directly), 'channel', 'playlist', 'search', 'hashtag'."
        },
        {
          "name": "position",
          "type": "null",
          "description": "1-based position in the source list (search rank, playlist index, channel order); null for direct videos."
        },
        {
          "name": "scrapedAt",
          "type": "string",
          "description": "When the row was read, ISO 8601."
        },
        {
          "name": "input",
          "description": "Error rows: the URL, id or search term as given."
        },
        {
          "name": "code",
          "description": "Error rows: not_found (private, removed, unknown channel or playlist), no_match (nothing found for the input), unavailable (age-restricted, members-only, temporary error), blocked (rate limited, retry later), invalid_input."
        },
        {
          "name": "message",
          "description": "Error rows: what happened, in plain English."
        }
      ],
      "sampleRows": [
        {
          "rowType": "video",
          "platform": "youtube",
          "videoId": "dQw4w9WgXcQ",
          "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
          "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
          "channelName": "Rick Astley",
          "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
          "channelHandle": "@RickAstleyYT",
          "channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
          "publishDate": "2009-10-24T23:57:33-07:00",
          "publishDateIsApprox": false,
          "publishedText": null,
          "durationSeconds": 213,
          "viewCount": 1829080503,
          "likeCount": 19491607,
          "commentCount": null,
          "category": "Music",
          "isShort": false,
          "isLive": false,
          "isLiveContent": false,
          "isUpcoming": false,
          "isUnlisted": false,
          "isFamilySafe": true,
          "keywords": [
            "rick astley",
            "Never Gonna Give You Up"
          ],
          "hashtags": [
            "rickastley"
          ],
          "soundTitle": null,
          "soundArtist": null,
          "description": "The official video for “Never Gonna Give You Up” by Rick Astley. ...",
          "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
          "thumbnailMaxUrl": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/maxresdefault.webp",
          "captionLanguages": [
            {
              "code": "en",
              "name": "English",
              "isAutoGenerated": false
            },
            {
              "code": "en",
              "name": "English",
              "isAutoGenerated": true
            }
          ],
          "hasCaptions": true,
          "detailLevel": "full",
          "source": null,
          "sourceType": "video",
          "position": null,
          "scrapedAt": "2026-10-22T08:00:00.000Z"
        }
      ],
      "prices": {
        "events": {
          "video": {
            "usd": 0.00125,
            "title": "Video"
          },
          "short": {
            "usd": 0.00125,
            "title": "Short"
          },
          "video-listing": {
            "usd": 0.0005,
            "title": "Video (fast listing mode)"
          }
        },
        "primaryEvent": "video",
        "startUsdPerRun": 0.001,
        "minCallUsd": 0.005
      },
      "typicalSecs": null,
      "status": "listed",
      "updatedAt": "2026-11-01T22:43:51.000Z"
    }
  ]
}
