Skip to main content
RTILA Marketplace

AppSumo Product Reviews Scraper - project configuration

Collect AppSumo review text, ratings, dates, reviewer flags and founder replies from every review page.

Market Research Intermediate Experimental v1.0.0 Updated Updated 2026-10-06 11 min read
Download
  • Windows x86_64

    BOT_appsumo-product-reviews-scraper.exe

    64.5 MB

    Download
  • macOS Apple Silicon chmod +x

    BOT_appsumo-product-reviews-scraper.sh

    58.0 MB

    Download
  • macOS Intel chmod +x

    BOT_appsumo-product-reviews-scraper.sh

    64.9 MB

    Download
  • Linux x86_64 chmod +x

    BOT_appsumo-product-reviews-scraper.sh

    82.3 MB

    Download

Do not rename the downloaded file - the filename carries the bot's configuration id.

This tab presents the complete project configuration exactly as published - the same JSON the launcher fetches at launch - ready to read, copy, or import into the desktop product. Back to the overview tab.

Project configuration

Full project JSON for appsumo-product-reviews-scraper

Canonical byte length: 13,664 - the length of the canonical form the digest covers.

Canonical digest: 460d4665b5fbcafc90102dcdddbedf3414baff951c8be9dccd4556ce1e13a1b8

About the delivered configuration encoding

At launch the backend delivers this configuration to the launcher as plain JSON. The block below is the complete project definition the launcher receives.

What you are reading has machine-readable equivalents, and the JSON at those routes is the same document as the JSON rendered on this page: the listing overview, the per-listing detail document and the configuration document. The digest and byte count shown in the panel header are computed over that document's canonical JSON form - recursively sorted keys, two-space indentation, no trailing newline - which is the form the provisioning pipeline signs and the reconcile control re-digests; it differs from the highlighted block above in key order and by that trailing newline, so hashing the visible text will not reproduce the digest.

Download this JSON
{
  "name": "AppSumo Product Reviews Scraper",
  "settings": {
    "urls": [
      "${product_urls}"
    ],
    "headless": false,
    "navigation_timeout": 60000,
    "max_concurrent_workers": 1,
    "humanoidEnabled": false,
    "proxyEnabled": false,
    "captchaEnabled": false,
    "matchTimezoneAndLocale": false,
    "block_resources": false,
    "get_urls_from_sitemap": false,
    "respect_robots_txt": false,
    "skip_initial_navigation": false,
    "variables": [
      {
        "name": "product_urls",
        "required": true,
        "is_list": false
      },
      {
        "name": "max_pages_per_product",
        "required": false,
        "is_list": false
      }
    ]
  },
  "datasets": {
    "reviews": {
      "item_selector": "css=[data-testid='review-card-wrapper']",
      "properties": [
        {
          "name": "product_slug",
          "type": "text",
          "selector": "variable=product_slug",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "source_url",
          "type": "text",
          "selector": "variable=source_url",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "scraped_page_url",
          "type": "page_url",
          "selector": ""
        },
        {
          "name": "scraped_page_number",
          "type": "page_url",
          "selector": "",
          "transformations": [
            {
              "type": "regex",
              "pattern": "[?&]page=(\\d+)",
              "default_value": "1"
            },
            {
              "type": "cast"
            }
          ]
        },
        {
          "name": "review_position_on_page",
          "type": "index",
          "selector": ""
        },
        {
          "name": "review_id",
          "type": "attribute",
          "selector": "css=a[target='_blank'][href*='/reviews/']",
          "attribute": "href",
          "transformations": [
            {
              "type": "regex",
              "pattern": "-?(\\d+)\\/?$",
              "default_value": ""
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_url",
          "type": "attribute",
          "selector": "css=a[target='_blank'][href*='/reviews/']",
          "attribute": "href",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_slug",
          "type": "attribute",
          "selector": "css=a[target='_blank'][href*='/reviews/']",
          "attribute": "href",
          "transformations": [
            {
              "type": "regex",
              "pattern": "\\/reviews\\/([^\\/?#]+)\\/?$",
              "default_value": ""
            }
          ]
        },
        {
          "name": "review_title",
          "type": "text",
          "selector": "css=div.space-y-1 p.font-header",
          "transformations": [
            {
              "type": "replace"
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_text",
          "type": "text",
          "selector": "css=div.text-midnight [data-testid='toggle-text']",
          "transformations": [
            {
              "type": "replace"
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_text_length",
          "type": "text",
          "selector": "css=div.text-midnight [data-testid='toggle-text']",
          "transformations": [
            {
              "type": "script",
              "code": "return String((value || '').length);",
              "default_value": "0"
            },
            {
              "type": "cast"
            }
          ]
        },
        {
          "name": "review_rating_raw",
          "type": "attribute",
          "selector": "css=[alt$=' stars']",
          "attribute": "alt",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_rating",
          "type": "attribute",
          "selector": "css=[alt$=' stars']",
          "attribute": "alt",
          "transformations": [
            {
              "type": "regex",
              "pattern": "([\\d.]+)",
              "default_value": ""
            },
            {
              "type": "cast"
            }
          ]
        },
        {
          "name": "review_date",
          "type": "text",
          "selector": "css=div.flex-wrap.text-grace span",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "review_date_iso",
          "type": "text",
          "selector": "css=div.flex-wrap.text-grace span",
          "transformations": [
            {
              "type": "script",
              "code": "var t=(value||'').trim();if(!t)return '';var d=new Date(t);if(!isNaN(d.getTime()))return d.toISOString().slice(0,10);var m=t.match(/^([A-Za-z]{3,9})\\.?\\s+(\\d{1,2}),\\s*(\\d{4})$/);if(!m)return '';var mo={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12}[m[1].slice(0,3).toLowerCase()];if(!mo)return '';return m[3]+'-'+('0'+mo).slice(-2)+'-'+('0'+m[2]).slice(-2);",
              "default_value": ""
            }
          ]
        },
        {
          "name": "review_meta_row_text",
          "type": "text",
          "selector": "css=div.flex-wrap.text-grace",
          "transformations": [
            {
              "type": "replace"
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "reviewer_username",
          "type": "text",
          "selector": "css=[data-testid='discussion-user-info'] a.font-bold",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "reviewer_profile_url",
          "type": "attribute",
          "selector": "css=[data-testid='discussion-user-info'] a.font-bold",
          "attribute": "href",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "reviewer_avatar_url",
          "type": "attribute",
          "selector": "css=[data-testid='discussion-user-info'] img",
          "attribute": "src",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "is_plus_member",
          "type": "count",
          "selector": "css=[data-testid='plus-badge']"
        },
        {
          "name": "is_verified_buyer",
          "type": "count",
          "selector": "css=[data-testid='discussion-user-info'] svg[data-icon='circle-check']"
        },
        {
          "name": "reviewer_verified_label",
          "type": "text",
          "selector": "css=[data-testid='discussion-user-info']",
          "transformations": [
            {
              "type": "regex",
              "pattern": "(Verified(?:\\s+purchaser)?)",
              "default_value": ""
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "reviewer_member_since",
          "type": "text",
          "selector": "css=[data-testid='discussion-user-info']",
          "transformations": [
            {
              "type": "regex",
              "pattern": "Member since:\\s*([A-Za-z]{3,9}\\.?\\s+\\d{4})",
              "default_value": ""
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "reviewer_badges_text",
          "type": "text",
          "selector": "css=[data-testid='discussion-user-info']",
          "transformations": [
            {
              "type": "replace"
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "has_founder_reply",
          "type": "count",
          "selector": "css=[data-testid='discussion-creator-reply']"
        },
        {
          "name": "founder_reply_label",
          "type": "text",
          "selector": "css=[data-testid='creator-reply-label']",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "founder_username",
          "type": "text",
          "selector": "css=[data-testid='creator-username']",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "founder_avatar_url",
          "type": "attribute",
          "selector": "css=[data-testid='creator-avatar']",
          "attribute": "src",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "founder_reply_date",
          "type": "text",
          "selector": "css=[data-testid='creator-posted-date']",
          "transformations": [
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "founder_reply_date_iso",
          "type": "text",
          "selector": "css=[data-testid='creator-posted-date']",
          "transformations": [
            {
              "type": "script",
              "code": "var t=(value||'').trim();if(!t)return '';var d=new Date(t);if(!isNaN(d.getTime()))return d.toISOString().slice(0,10);var m=t.match(/^([A-Za-z]{3,9})\\.?\\s+(\\d{1,2}),\\s*(\\d{4})$/);if(!m)return '';var mo={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12}[m[1].slice(0,3).toLowerCase()];if(!mo)return '';return m[3]+'-'+('0'+mo).slice(-2)+'-'+('0'+m[2]).slice(-2);",
              "default_value": ""
            }
          ]
        },
        {
          "name": "founder_reply_text",
          "type": "text",
          "selector": "css=[data-testid='discussion-creator-reply'] [data-testid='toggle-text']",
          "transformations": [
            {
              "type": "replace"
            },
            {
              "type": "trim"
            }
          ]
        },
        {
          "name": "helpful_count",
          "type": "text",
          "selector": "css=[data-testid='discussion-helpful-buttons']",
          "transformations": [
            {
              "type": "regex",
              "pattern": "(\\d+)",
              "default_value": ""
            },
            {
              "type": "cast"
            }
          ]
        },
        {
          "name": "review_share_count",
          "type": "text",
          "selector": "css=[data-testid='review-card-wrapper']",
          "transformations": [
            {
              "type": "regex",
              "pattern": "Share(\\d+)",
              "default_value": ""
            },
            {
              "type": "cast"
            }
          ]
        }
      ],
      "required_fields": [
        "review_id",
        "review_text"
      ]
    }
  },
  "commands": [
    {
      "command": "execute_script",
      "params": {
        "script": "return location.href;"
      },
      "output_variable": "source_url"
    },
    {
      "command": "execute_script",
      "params": {
        "script": "return (location.pathname.split('/products/')[1] || '').split('/')[0];"
      },
      "output_variable": "product_slug"
    },
    {
      "command": "goto",
      "params": {
        "url": "https://appsumo.com/products/${product_slug}/reviews/?ref=pdp&page=1"
      }
    },
    {
      "command": "wait_for_selector",
      "params": {
        "selector": "css=h1, [data-testid='review-card-wrapper']",
        "timeout": 45000
      }
    },
    {
      "command": "execute_script",
      "params": {
        "script": "var as=document.getElementsByTagName('a');var last=1;for(var i=0;i<as.length;i++){var l=as[i].getAttribute('aria-label');if(l&&l.indexOf('Go to page ')===0){var m=l.match(/(\\d+)$/);if(m){var n=parseInt(m[1],10);if(n>last)last=n;}}}return last;"
      },
      "output_variable": "last_page"
    },
    {
      "command": "set_variable",
      "params": {
        "name": "page",
        "value": 1
      }
    },
    {
      "command": "extract_data",
      "params": {
        "dataset": "reviews"
      }
    },
    {
      "command": "while",
      "params": {
        "condition": {
          "type": "script",
          "expression": "((variables.page - 0) || 0) < ((variables.last_page - 0) || 0) && ((variables.page - 0) || 0) < ((variables.max_pages_per_product - 0) || 999)"
        },
        "do": [
          {
            "command": "set_variable",
            "params": {
              "name": "page",
              "value": "${(variables.page - 0) + 1}"
            }
          },
          {
            "command": "goto",
            "params": {
              "url": "https://appsumo.com/products/${product_slug}/reviews/?ref=pdp&page=${page}"
            }
          },
          {
            "command": "wait_for_selector",
            "params": {
              "selector": "css=[data-testid='review-card-wrapper']",
              "timeout": 45000
            }
          },
          {
            "command": "extract_data",
            "params": {
              "dataset": "reviews"
            }
          }
        ]
      }
    }
  ],
  "authors": []
}

Frequently asked questions

How do I scrape AppSumo product reviews to CSV?
Run the launcher, paste one or more AppSumo product URLs into the required product_urls list (one URL per line), optionally set a per-product page cap, and the bot fans the list out, normalizes each URL to the AppSumo review tab, paginates to the last page and extracts every review card into the reviews dataset. Rows land in the launcher's local results store; export them from the results view - CSV is the common working format, and JSON and XLSX exports are available too. No selector work is required from you.
What review data does the AppSumo scraper collect?
Thirty-three fields per review: the product slug and source URL, the scraped page URL and number, the review position, review id, url and slug, the title, the full review text and its character length, the raw star label and the numeric rating, the displayed date and an ISO-normalised date, the meta row text, the reviewer username, profile url and avatar, verified-buyer and AppSumo Plus member flags, the verified label and member-since text, the badge row, whether a founder replied plus the reply label, username, avatar, date, ISO date and text, and the helpful and share counts.
How does the bot handle multiple AppSumo products at once?
product_urls is a required list variable, and the engine fans it out into its URL queue with one worker task per entry, so a dozen products become a dozen independent collection passes in a single run. It accepts any AppSumo product URL shape - with or without the reviews path, with or without a trailing slash or query string - and normalizes each to the canonical review tab. One caution baked into the configuration: the value must stay a JSON array. Passed as a single string, the fan-out breaks and the run collapses to one worker-replicated URL.
How does AppSumo review pagination work?
For each product the bot first reads the page's own pagination controls - it scans links whose accessible label reads Go to page followed by a number and takes the highest - storing that in last_page. It then loops from page one up to last_page, re-navigating to each numbered review page URL, waiting for a review card to render, and extracting. The loop stops at the last detected page or at the max_pages_per_product cap, whichever comes first.
Can I limit how many review pages are crawled per product?
Yes. max_pages_per_product is an optional safety cap on review pages crawled per product. It defaults to 999, which is effectively unlimited, so a full review history is collected. Set it to 2 for a fast smoke test, or to any number to bound run time and request volume on products with very deep review back-catalogs.
Does the scraper collect reviewer names and profiles?
It does, deliberately, and that raises the compliance bar. The dataset captures reviewer_username, reviewer_profile_url and reviewer_avatar_url, plus verified-buyer and AppSumo Plus member flags and a member-since value. These identify people, so this listing is flagged piiHandled true: establish a lawful basis before storing or analysing them, honour takedowns, and never republish reviewer identities or use them for spam.
Can it capture AppSumo founder replies to reviews?
Yes. has_founder_reply is a count flag for a creator response on the review card, and when one is present the run also captures founder_reply_label, founder_username, founder_avatar_url, founder_reply_date and its ISO-normalised form, and founder_reply_text. Diffing founder_reply_date_iso against review_date_iso gives you a reply-latency measure - a useful responsiveness signal when benchmarking how a vendor handles its negative AppSumo reviews.
How are AppSumo review dates normalised?
Two ways per date. review_date keeps the displayed string verbatim, and review_date_iso runs it through a small script transform that first tries a native date parse and then falls back to a month-name pattern (AppSumo shows dates like March 4, 2026), emitting a clean yyyy-mm-dd value. The same pair exists for founder replies. When a date cannot be parsed the ISO field is left empty rather than guessed, so an empty review_date_iso is an honest unknown, not a failure.
How does deduplication work across review pages?
AppSumo review pages can overlap and re-render as you paginate, so the reviews dataset deduplicates on the combination of review_id, review_text and reviewer_username. That triple is stable for a given review and catches the same card appearing on adjacent pages. review_id and review_text are also declared required fields, so a card that never rendered its body is logged rather than silently trusted.
Is scraping AppSumo reviews allowed?
Automated collection may conflict with AppSumo's terms of service, and review text plus reviewer usernames are personal data in most jurisdictions. You are responsible for reviewing AppSumo's terms and establishing a lawful basis under GDPR, UK GDPR or your local equivalent before running the bot, storing the output, or analysing named individuals. Keep request volume modest and let the acceptable-use policy govern every run. This bot is not affiliated with or endorsed by AppSumo.
Why are some rating or date fields empty?
Empty is normal and expected in a few cases: a review with no star label leaves review_rating null through its cast default; an unparseable or absent date leaves review_date_iso empty; helpful_count and review_share_count fall back to 0 when the card shows none; the AppSumo Plus flag reads 0 for non-members; and the founder-reply fields are empty on every review that has no creator response. The transforms fall back to empty or zero rather than failing the row, and the failure contract is loud - empty values or zero rows, never fabricated data.
How long does a full AppSumo review scrape take?
It scales with products times pages. Each product costs one page visit per review page, paced by human-like navigation and a wait for the review card to render before each extraction. A single product with a few hundred reviews across a handful of pages finishes in minutes; a queue of many products with deep review histories can run toward an hour. Lower max_pages_per_product or queue fewer products to keep runs short.
Does the bot need an internet connection to run?
Yes on first run: the launcher fetches its configuration by id from the backend at launch, so an internet connection is required at least the first time it starts.
Can I rename the downloaded file?
No. The downloaded file must keep its exact name - the filename carries the per-bot configuration id, and renaming it breaks the configuration fetch at launch.