# ResellerRatings Scraper \[$2.10/1K💰] | Merchants | Reviews (`ahmed_jasarevic/resellerratings-scraper`) Actor

Scrape merchant profiles and full review archives from ResellerRatings - store ratings, sub-ratings, verified-buyer flags, review text, votes, sentiment and merchant replies. Discover 35,500+ sellers from the sitemap. No browser needed.

- **URL**: https://apify.com/ahmed_jasarevic/resellerratings-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.05 / 1,000 reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## ResellerRatings Scraper — Merchant Profiles and the Full Verified-Review Archive

Scrape **merchant/store profiles and the complete review archive** from `resellerratings.com` — store rating, review count, review text, star score, date, author, **verified-buyer flag**, per-aspect sub-ratings, engagement votes, sentiment, geo and **merchant replies**. Around **35,500 merchants** are discoverable through the seller sitemap.

Built for ecommerce competitor research, brand due diligence, review sentiment analysis and merchant benchmarking. **No browser, no Cloudflare challenge, no paid anti-bot API.**

***

### Main use cases

- **Ecommerce competitor research** — pull a category's merchants with their full review archives in one run
- **Merchant due diligence** — review volume, reply rate, verified-buyer share and sub-ratings per store before you list with them
- **Review sentiment and topic analysis** — text, scores, per-aspect pricing/shipping/service sub-ratings and helpful-vote counts
- **Merchant benchmarking** — rank sellers on `percentReply`, `positivePct` and `lifetimeRating`
- **Merchant outreach lists** — merchant websites and categories for every crawled store
- **Fake-review detection research** — verified-buyer flags, member flags and rating distributions across merchants

***

### Data source: the site's own internal JSON API (content-negotiation bypass)

`resellerratings.com` is an Apache/PHP application that server-side-renders a React SPA. The only blob in the served HTML is a config stub (`<div id="app" data-config='{...}'>`), so the store data itself is not in the HTML. Reverse-engineering `store.app.js` revealed the SPA's data layer: it requests the **same public page paths** but with

```
Accept: application/json
X-Requested-With: XMLHttpRequest
```

and the backend then answers with the **complete serialized SSR state as JSON** instead of an HTML shell (`Content-Type: application/json`). This is the classic Symfony-style content-negotiation endpoint pattern, exposed here without any auth. One `curl` with two headers returns roughly 50 KB of clean, structured store and review data.

#### Endpoints used — all public, all unauthenticated

| Purpose | Endpoint |
|---|---|
| Store profile + reviews, page 1 | `GET https://www.resellerratings.com/store/{seoName}` |
| Store profile + reviews, page N | `GET https://www.resellerratings.com/store/{seoName}/page/{n}` |
| Same, with sort | `GET https://www.resellerratings.com/store/{seoName}/sort/{sort}/page/{n}` |
| Single review thread | `GET https://www.resellerratings.com/store/{seoName}/review/{commentId}` |
| Store discovery / ranking | `GET https://www.resellerratings.com/search?category={cat}&sort={sort}&page={n}` |
| Category list | `GET https://www.resellerratings.com/endpoint/category?minimum_seller_count=0` |
| Store URL discovery (≈35.5k merchants) | `GET https://www.resellerratings.com/sitemap/seller_info_sitemap_index.xml` → `seller_info_sitemap{1,2}.xml.gz` |

Verify it yourself:

```bash
curl -s 'https://www.resellerratings.com/store/Pro_star' \
  -H 'Accept: application/json' \
  -H 'X-Requested-With: XMLHttpRequest' | jq '.store.storeData.rating'
```

**Why this surface was chosen:** it is cheaper than static HTML parsing (nothing to select on), far cheaper and far more reliable than Playwright (no browser units, no rendering), and it dodges every bot-mitigation layer — the origin is plain Apache behind CloudFront with rate limiting only, no Cloudflare Bot Management, no DataDome, no Akamai, no Kasada. Because the JSON *is* the SSR state, the data is also complete: pagination metadata (`paginate.totalPages`), rating distribution, sub-ratings, tags and merchant replies all arrive in one request.

#### robots.txt

Only `/store/*/review/*`, `/viewschema/*` and `/help/*` are disallowed. This Actor crawls `/store/{seo}` and `/store/{seo}/page/{n}`, which are allowed. `/store/{seo}/review/{id}` is never requested.

***

### Input

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `startUrls` | array | no | `["https://www.resellerratings.com/store/Pro_star"]` | Store profile URLs, **or** category landing pages like `https://www.resellerratings.com/category/adult`. Bare slugs (`"Pro_star"`, `"adult"`) also work. Ignored if empty. |
| `storeSlugs` | array | no | `["Pro_star","JustAnswer"]` | Store SEO names. Used when `startUrls` is empty. |
| `categoryUrls` | array | no | `["https://www.resellerratings.com/category/adult"]` | Category landing pages whose stores should be scraped. Each URL is read as JSON, the merchants it lists are ranked, and the top `maxStores` are crawled. Bare slugs work too. |
| `categories` | array | no | `[]` | Category slugs to expand through `/search`, e.g. `["adult"]`, `["Computers & Electronics"]`. Unlike `categoryUrls`, this walks **every** `/search` page, so it finds more merchants in the same category. |
| `discoverySort` | string | no | `reviewCount` | Which stores survive `maxStores`. See "Why discoverySort defaults to reviewCount" below. `site` keeps the endpoint order. |
| `minReviewsPerStore` | integer | no | `0` | Skip discovered stores advertising fewer than this many reviews. `0` = no filter. Use it with a high `maxStores` to crawl only merchants worth paginating. |
| `maxDiscoveryPages` | integer | no | `5` | `/search` result pages read per category before ranking (20 stores per page, max 50). Ignored for `categoryUrls`, whose landing page is not paginated. |
| `searchSort` | string | no | `overallscore desc` | Sort passed to `/search` when `discoverySort` is `site`. Try `overallscore desc`, `reviewcount desc`, `replyrate desc`. |
| `useSitemap` | boolean | no | `true` | When no `startUrls` / `storeSlugs` / `categories` are given, seed store URLs from the seller sitemap index (≈35.5k merchants). Set to `false` for an empty run. |
| `sort` | string | no | `""` (site default = newest first) | Review sort: `date`, `rating`, `overallscore`, `relevance`, `updated`. |
| `mode` | string | no | `all` | `all` (store profiles + reviews) / `stores` (merchant profiles only) / `reviews` (review archive only). |
| `maxStores` | integer | no | `10` | Cap on stores crawled per discovery source (max 2000). Applied **after** `discoverySort` ranking. |
| `maxReviewsPerStore` | integer | no | `50` | Stop paginating a store after this many reviews. |
| `maxRequestsPerCrawl` | integer | no | `200` | Hard total HTTP request cap. Each store page holds ~15 reviews, so budget roughly `maxRequestsPerCrawl / 4` stores, plus `maxDiscoveryPages`. |
| `proxy` | object | no | `useApifyProxy: false` | Standard Apify proxy picker. |

#### Seeding precedence

1. `startUrls` / `storeSlugs` → direct store URLs
2. `categoryUrls` and `/category/` URLs inside `startUrls` → `/category` + `/search` discovery
3. `categories` → `/search` discovery (paginated, honours `maxStores`)
4. `useSitemap` → `seller_info` sitemap index (≈35.5k merchants)

#### Why `discoverySort` defaults to `reviewCount`

ResellerRatings' own category order is by **score**, not by review volume. In the `adult` category its default `overallscore desc` listing opens with shops rated 10.00 that have **one** review each:

| Rank | Store | Rating | Reviews |
|---|---|---|---|
| 1 | Spotted_Horse | 10.00 | 4 |
| 2 | Uflash | 10.00 | 1 |
| 3 | JToutlet | 10.00 | 1 |

Taking the first 10 of those returns **17 reviews in total**. Ranking the same category by review count instead picks the merchants that actually have archives:

| Rank | Store | Rating | Reviews |
|---|---|---|---|
| 1 | Betweenthesatinsheets_com | 9.18 | 572 |
| 2 | PinkCherry_Sex_Toys_Canada | 8.54 | 467 |
| 3 | EdenFantasys | 9.98 | 444 |

Same `maxStores: 10`, same `maxReviewsPerStore: 50` — **506 items instead of 27**. Every discovery response carries `reviewCount` per merchant, so the Actor reads every `/search` page it needs, ranks locally, and only then applies `maxStores`. Set `discoverySort` to `site` if you want the original site order back.

***

### Output

Two separate datasets, so each data type gets its own table and schema in the **Output** tab. Switch between them with the view selector, or export each from the **Storage** tab.

| Output | Dataset | Rows |
|---|---|---|
| **Reviews** | default dataset | one row per review |
| **Stores** | `stores` dataset | one row per crawled merchant |

Every row also carries an `itemType` field (`"store"` or `"review"`) so you can tell them apart if you merge the datasets yourself.

#### Stores dataset

| Group | Fields |
|---|---|
| Identity | `storeId`, `storeName`, `seoName`, `storeUrl`, `storeThumb` |
| Rating | `rating`, `starRating`, `lifetimeRating`, `sixMonthRating`, `yearlyRating` |
| Volume | `reviewCount`, `lifetimeTotal`, `sixMonthTotal`, `yearlyTotal` |
| Distribution | `ratingDistribution` (1–5 star counts), `positivePct`, `neutralPct`, `negativePct` |
| Response behaviour | `percentReply`, `hasMerchantReplies` |
| Sub-ratings | `infoRating` — pricing / shipping / customer service / return policy / future |
| Plan flags | `isPremium`, `isElite`, `currentPlan`, `isCollecting` |
| Contact | `merchantWebsite`, `merchantDescription`, `categories`, `categoryNames`, `supportReps`, `supportRepNames`, `compareUrl` |

#### Reviews dataset

| Group | Fields |
|---|---|
| Identity | `reviewId`, `surveyId`, `reviewUrl` |
| Store context | `storeId`, `storeName`, `storeSeoName`, `storeUrl` |
| Author | `author`, `authorUsername`, `authorId`, `authorAvatar`, `isMember` |
| Verification | **`badgeType`**, **`isVerifiedBuyer`** |
| Body | `rating`, `title`, `text`, `firstSentence`, `date`, `postedDate`, `wasEdited` |
| Sub-ratings | `subRatings` — pricing / customer service / shipping |
| Tags | `tags` |
| Engagement | `helpfulVotes`, `notHelpfulVotes`, `sentiment`, `isFlagged` |
| Geo | city / state / country |
| Merchant replies | `merchantReplies[]` — `{date, author, body, kind, isMerchant, avatar, sellerId}` |
| Store-level repeat | `storeRating`, `storeReviewCount` |

`merchantReplies` combines the API's `repReply` object with any threaded `reviewComments`, so you can see how a merchant handled a complaint. `isVerifiedBuyer` is derived from the API's `badgeType === "Verified"`, with `isMember` as a fallback signal.

***

### Example input

Direct store plus a category to expand:

```json
{
  "startUrls": ["https://www.resellerratings.com/store/Pro_star"],
  "categoryUrls": ["https://www.resellerratings.com/category/adult"],
  "maxStores": 25,
  "maxReviewsPerStore": 100
}
```

Full category sweep ranked by review volume (the highest-yield mode):

```json
{
  "categories": ["Computers & Electronics"],
  "discoverySort": "reviewCount",
  "minReviewsPerStore": 200,
  "maxStores": 50,
  "maxDiscoveryPages": 10,
  "maxReviewsPerStore": 200,
  "maxRequestsPerCrawl": 2000
}
```

Merchant profiles only — no review pages fetched, so no `review` events:

```json
{ "mode": "stores", "categories": ["adult"], "maxStores": 100 }
```

***

### Example output

Store row:

```json
{
  "itemType": "store",
  "storeId": "123456",
  "storeName": "Pro Star",
  "seoName": "Pro_star",
  "storeUrl": "https://www.resellerratings.com/store/Pro_star",
  "rating": 4.42,
  "starRating": 4.4,
  "lifetimeRating": 4.3,
  "sixMonthRating": 4.5,
  "reviewCount": 2871,
  "lifetimeTotal": 2871,
  "ratingDistribution": { "1": 96, "2": 41, "3": 63, "4": 210, "5": 2461 },
  "positivePct": 86.4,
  "neutralPct": 5.1,
  "negativePct": 8.5,
  "percentReply": 92,
  "hasMerchantReplies": true,
  "isPremium": true,
  "isElite": false,
  "currentPlan": "Business",
  "merchantWebsite": "https://prostar.example",
  "categoryNames": ["Computers & Electronics"],
  "scrapedAt": "2026-09-29T09:00:00.000Z"
}
```

Review row:

```json
{
  "itemType": "review",
  "reviewId": "882131",
  "storeId": "123456",
  "storeName": "Pro Star",
  "storeSeoName": "Pro_star",
  "author": "verified_buyer_4471",
  "isMember": true,
  "badgeType": "Verified",
  "isVerifiedBuyer": true,
  "rating": 5,
  "title": "Exactly as described",
  "text": "Arrived two days early and in perfect condition. Would order from this seller again.",
  "firstSentence": "Arrived two days early",
  "date": "2026-08-19",
  "postedDate": "2026-08-19",
  "wasEdited": false,
  "subRatings": { "pricing": 5, "customerService": 5, "shipping": 5 },
  "helpfulVotes": 12,
  "notHelpfulVotes": 1,
  "sentiment": "positive",
  "city": "Austin",
  "state": "TX",
  "merchantReplies": [
    {
      "date": "2026-08-20",
      "author": "Pro Star Support",
      "body": "Thanks so much for the kind words — glad it arrived early!",
      "kind": "repReply",
      "isMerchant": true
    }
  ],
  "storeRating": 4.42,
  "storeReviewCount": 2871,
  "scrapedAt": "2026-09-29T09:00:02.000Z"
}
```

***

### Monitor merchant reputation and review volume on a schedule

Every Apify Actor is callable over HTTP, so the useful pattern is a recurring crawl of the same merchant set:

- **Weekly** — same `storeSlugs`; diff `reviewCount`, `rating` and `sixMonthRating` to track reputation drift
- **Monthly** — re-crawl a category with `discoverySort: reviewCount` to catch merchants entering or leaving the top of a niche
- **Alerting** — filter on `negativePct` or `isFlagged` reviews and push runs to a webhook when a merchant crosses your threshold
- **Warehouse sync** — Apify webhooks, Zapier, Make, Google Sheets and 100+ integrations fire on run completion; both datasets export as JSON, CSV, Excel, XML, HTML or RSS

Set `mode` per saved input so merchant-only monitoring and full review collection stay on separate schedules.

***

### Related Actors

- [G2 Reviews & Products Scraper](https://apify.com/automation-lab/g2-scraper) (`automation-lab/g2-scraper`) — 5M+ software reviews with NPS, sentiment and switching data
- [Trustpilot Scraper](https://apify.com/ahmed_jasarevic/trustpilot-reviews-scraper) (`ahmed_jasarevic/trustpilot-reviews-scraper`) — reviews and ratings from the largest independent review platform
- [Reviews.io Scraper](https://apify.com/ahmed_jasarevic/reviews-scraper) (`ahmed_jasarevic/reviews-scraper`) — exact review dates and vote counts
- [ConsumerAffairs Scraper](https://apify.com/ahmed_jasarevic/consumeraffairs-reviews-scraper) (`ahmed_jasarevic/consumeraffairs-reviews-scraper`) — reviews plus complaint records
- [Better Scraper](https://apify.com/ahmed_jasarevic/better-scraper) (`ahmed_jasarevic/better-scraper`) — BBB business profiles, ratings and reviews for US company due diligence

***

### FAQ

#### Why not scrape the HTML pages directly?

The served HTML contains only a React config stub — no store data at all. The data lives behind the SPA's own data layer, which requests the same paths with `Accept: application/json`. Going straight there removes the browser entirely: no rendering, no CSS selectors to maintain, a fraction of the compute, and complete payloads including pagination metadata, rating distribution, sub-ratings, tags and merchant replies in a single request.

#### What are the alternatives to this Actor?

- **A Playwright-based scraper** — roughly 50× the compute cost per run, and it still has to render a React SPA before any data appears.
- **Scraping Google or Trustpilot merchant pages** — different, smaller datasets, and you would lose ResellerRatings' verified-buyer badges and merchant-reply threads.
- **Buying review data from a reseller** — you get a snapshot, not a live archive you can schedule against.

#### Why does `discoverySort` default to `reviewCount` instead of the site's own order?

Because the site's order ranks by score, not volume, and a merchant with one 5-star review outranks one with 500 reviews. Crawling the first 10 of the site's default `adult` listing returns **17 reviews total**; ranking the same category by review count returns **506 items** at the same settings. The Actor therefore reads every `/search` page it needs, ranks locally on `reviewCount`, and only then applies `maxStores`.

#### What is the difference between `categoryUrls` and `categories`?

`categoryUrls` reads one category landing page and ranks the merchants listed on it. `categories` walks every `/search` result page for that category (honouring `maxDiscoveryPages`), which finds far more merchants in the same category — at the cost of more list requests. Use `categories` when you want breadth within a niche.

#### How do I get more merchants than the default 10?

Raise `maxStores` (up to 2000) and `maxRequestsPerCrawl` together, and raise `maxDiscoveryPages` if you are using `categories`. Each store costs roughly 4 requests (profile plus review pages), so budget `maxRequestsPerCrawl / 4` stores.

#### What is `useSitemap` for?

When you supply no store URLs and no categories, it seeds the run from the `seller_info` sitemap index — around 35,500 merchants. Set it to `false` for a deliberate empty run instead of an accidental 10-store crawl of arbitrary merchants.

#### Do I need a proxy?

No. The origin is Apache behind AWS CloudFront with an `rsrsession` cookie and ordinary rate limiting. No Cloudflare, Akamai, DataDome or Kasada fingerprinting was observed. Enable **Apify Proxy** in the run input only if you hit throttling at high volume — no code change needed.

#### How much does it cost?

Store rows are billed at **$0.0021–$0.0025 each** (roughly **$2.10–$2.50 per 1,000 merchants**) and review rows at **$0.00105–$0.0012 each** (roughly **$1.05–$1.20 per 1,000 reviews**), as two independently priced events. `mode: "stores"` charges only store events and `mode: "reviews"` charges only review events, so a merchant-profile-only run costs the store rate alone. Exactly one event fires per delivered row, and charging happens *before* the row is written, so nothing unpaid ever reaches a dataset. Runs stop cleanly once your charge limit is reached. Free accounts get a capped 20-item preview.

***

### For AI Agents & LLM Apps

Callable via the Apify MCP server and the Apify API. Returns merchant profiles (second dataset, `stores`) and the review archive (default dataset), with `itemType` on every row.

**Minimal valid input** — two known stores, default limits:

```json
{ "storeSlugs": ["Pro_star", "JustAnswer"] }
```

**Highest-yield category sweep** — ranked by review volume so `maxStores` buys real archives instead of one-review shops:

```json
{
  "categories": ["Computers & Electronics"],
  "discoverySort": "reviewCount",
  "minReviewsPerStore": 200,
  "maxStores": 50,
  "maxDiscoveryPages": 10,
  "maxReviewsPerStore": 200
}
```

**Merchant profiles only** — skips every review page and bills only the store event:

```json
{ "mode": "stores", "categories": ["adult"], "maxStores": 100 }
```

**Output fields** — stores: `storeId`, `storeName`, `seoName`, `storeUrl`, `storeThumb`, `rating`, `starRating`, `lifetimeRating`, `sixMonthRating`, `yearlyRating`, `reviewCount`, `lifetimeTotal`, `sixMonthTotal`, `yearlyTotal`, `ratingDistribution`, `positivePct`, `neutralPct`, `negativePct`, `percentReply`, `hasMerchantReplies`, `infoRating`, `isPremium`, `isElite`, `currentPlan`, `isCollecting`, `merchantWebsite`, `merchantDescription`, `categories`, `categoryNames`, `supportReps`, `supportRepNames`, `compareUrl`, `itemType`. Reviews: `reviewId`, `surveyId`, `reviewUrl`, `storeId`, `storeName`, `storeSeoName`, `storeUrl`, `author`, `authorUsername`, `authorId`, `authorAvatar`, `isMember`, `badgeType`, `isVerifiedBuyer`, `rating`, `title`, `text`, `firstSentence`, `date`, `postedDate`, `wasEdited`, `subRatings`, `tags`, `helpfulVotes`, `notHelpfulVotes`, `sentiment`, `isFlagged`, `merchantReplies`, `storeRating`, `storeReviewCount`, `itemType`.

**Behaviors an agent should know**

- **Seeding is strictly ordered:** `startUrls` / `storeSlugs` → `categoryUrls` → `categories` → `useSitemap`. Later sources are ignored if an earlier one produced input. Defaults mean all four can fire, so be explicit about what you want.
- **`maxStores` is applied *after* ranking, not before.** With the default `discoverySort: reviewCount` that is what you want; with `site` you get the site's own score-first order, which returns very few reviews per store.
- **`maxRequestsPerCrawl` is the real budget.** Each store costs roughly 4 requests, so 200 default requests buys about 50 stores, not 200.
- **`mode` controls which pay-per-event fires** — `all` charges both, `stores` charges only `store`, `reviews` charges only `review`. Set `mode: "stores"` for a cheap profile crawl.
- **`itemType` is on every row in both datasets** (`"store"` or `"review"`), so you can concatenate the two datasets and still filter correctly.
- **`isVerifiedBuyer` is derived**, not a raw field: it comes from `badgeType === "Verified"`, with `isMember` as the fallback signal. Check `badgeType` directly if you need the exact badge.
- **`merchantReplies` merges two sources** — the API's `repReply` object plus threaded `reviewComments` — so it is an array that may be empty.
- **Category slugs are free text and market-specific**, e.g. `"adult"` or `"Computers & Electronics"`. An unrecognised slug returns no merchants rather than an error.
- **`useSitemap` defaults to `true`.** With no store or category input you will get an arbitrary crawl of discovered merchants; set it to `false` for a clean empty run.

**Billing model** — pay per event with two independently priced events: `store` ($0.0021–$0.0025 per merchant row) and `review` ($0.00105–$0.0012 per review row), plus a per-run Actor Start event scaled by memory. The run stops cleanly when the caller's charge limit is reached.

***

### Legal & Compliance Disclaimer

This Actor is an independent third-party tool. It is not affiliated with, endorsed by, sponsored by, or connected to ResellerRatings or ShopperStream, Inc.

It reads **only publicly accessible JSON responses** from `www.resellerratings.com` public page paths. It does not log in, does not bypass authentication, does not solve CAPTCHAs, does not use proxies by default, and respects the site's `robots.txt` — only `/store/{seo}` and `/store/{seo}/page/{n}` are crawled; the disallowed `/store/*/review/*`, `/viewschema/*` and `/help/*` paths are never requested.

Output includes **reviewer usernames, avatars, geo fields and review text**, and store rows include **merchant website and support-representative names**. That is personal data. ResellerRatings publishes it publicly, but you are responsible for your own lawful basis for processing it, and for complying with the site's Terms of Service and any applicable data-protection law including GDPR, UK GDPR and CCPA. Do not use reviewer data for unsolicited commercial outreach, which can violate applicable law including CAN-SPAM and TCPA.

Please note that several merchants and consumer advocates have publicly disputed ResellerRatings' moderation practices and the authenticity of some listed reviews. If review authenticity matters to your use case, treat `isVerifiedBuyer` and `isFlagged` as signals to filter on, not as proof.

This Actor is a data-collection tool; it does not provide legal advice and makes no claim that any particular use is permitted.

***

### SEO Keywords

resellerratings scraper, reseller ratings data, merchant review data, verified buyer reviews, ecommerce review scraper, merchant profile scraper, product review api, review sentiment analysis, store rating data, customer review archive, review scraping api, competitor review analysis, brand due diligence ecommerce, merchant benchmarking, ecommerce competitor research, review sub ratings, merchant reply data, review data for ai training, trustpilot alternative data, review api alternative, ecommerce reputation monitoring, helpful votes review data, seller review dataset

# Actor input Schema

## `startUrls` (type: `array`):

Store profile URLs (https://www.resellerratings.com/store/Pro_star) or category landing pages (https://www.resellerratings.com/category/adult). Bare slugs such as "Pro_star" also work. Category URLs are discovered into their stores, then each store is crawled. Ignored if empty.

## `storeSlugs` (type: `array`):

Store SEO names to crawl, e.g. \["Pro_star", "JustAnswer"]. Used when startUrls is empty.

## `categoryUrls` (type: `array`):

Category landing pages whose stores should be scraped, e.g. \["https://www.resellerratings.com/category/adult"]. Each URL is read as JSON, the merchants it lists are ranked, and the top maxStores are crawled. Bare slugs ("adult") work too.

## `categories` (type: `array`):

Discover merchants by category slug through the internal /search JSON endpoint, e.g. \["adult"], \["Computers & Electronics"]. Unlike categoryUrls this walks every /search page, so it finds more merchants in the same category.

## `discoverySort` (type: `string`):

Which stores survive maxStores. 'reviewCount' (default) picks the merchants with the deepest review archives - the site's own default order ranks 10.00-rated shops with a single review first, which returns almost no reviews. 'site' keeps the endpoint order (see searchSort).

## `minReviewsPerStore` (type: `integer`):

Skip discovered stores that advertise fewer than this many reviews. 0 = no filter. Use it with a high maxStores to crawl only the merchants worth paginating.

## `maxDiscoveryPages` (type: `integer`):

How many /search result pages to read per category before ranking (20 stores per page). Raise it when a category is larger than the top stores you need. Ignored for categoryUrls, whose landing page is not paginated.

## `searchSort` (type: `string`):

Sort passed to /search when discoverySort is 'site'. Try: "overallscore desc", "reviewcount desc", "replyrate desc".

## `useSitemap` (type: `boolean`):

When no startUrls/storeSlugs/categories are given, seed store URLs from the seller_info sitemap index (~35.5k merchants). Set to false for an empty run.

## `sort` (type: `string`):

Optional review sort: date, rating, overallscore, relevance, updated. Empty = site default (newest first).

## `mode` (type: `string`):

all = store profiles + reviews (default). stores = merchant profiles only. reviews = review archive only.

## `maxStores` (type: `integer`):

Maximum number of stores to crawl per category/discovery source. Applied after discoverySort ranking.

## `maxReviewsPerStore` (type: `integer`):

Stop paginating a store after this many reviews.

## `maxRequestsPerCrawl` (type: `integer`):

Hard cap on total HTTP requests for this run. Each store page holds ~15 reviews, so budget roughly maxRequestsPerCrawl / 4 stores, plus maxDiscoveryPages.

## `proxy` (type: `object`):

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.resellerratings.com/store/Pro_star"
    }
  ],
  "storeSlugs": [
    "Pro_star",
    "JustAnswer"
  ],
  "categoryUrls": [
    "https://www.resellerratings.com/category/adult"
  ],
  "categories": [],
  "discoverySort": "reviewCount",
  "minReviewsPerStore": 0,
  "maxDiscoveryPages": 1,
  "searchSort": "overallscore desc",
  "useSitemap": false,
  "sort": "",
  "mode": "all",
  "maxStores": 10,
  "maxReviewsPerStore": 5,
  "maxRequestsPerCrawl": 200,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `reviews` (type: `string`):

One row per scraped review: author, rating, body, verified-buyer flag, merchant replies and geo.

## `stores` (type: `string`):

One row per crawled store: ratings, review volume, reply rate, plan flags and merchant profile data.

## `files` (type: `string`):

Default key-value store contents, including the run INPUT.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.resellerratings.com/store/Pro_star"
        }
    ],
    "storeSlugs": [
        "Pro_star",
        "JustAnswer"
    ],
    "categoryUrls": [
        "https://www.resellerratings.com/category/adult"
    ],
    "categories": [],
    "sort": "",
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/resellerratings-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://www.resellerratings.com/store/Pro_star" }],
    "storeSlugs": [
        "Pro_star",
        "JustAnswer",
    ],
    "categoryUrls": ["https://www.resellerratings.com/category/adult"],
    "categories": [],
    "sort": "",
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/resellerratings-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.resellerratings.com/store/Pro_star"
    }
  ],
  "storeSlugs": [
    "Pro_star",
    "JustAnswer"
  ],
  "categoryUrls": [
    "https://www.resellerratings.com/category/adult"
  ],
  "categories": [],
  "sort": "",
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call ahmed_jasarevic/resellerratings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/resellerratings-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HO2v9Hfs702Hvdle5/builds/tIPbfPHgTS2pOIfw6/openapi.json
