# ResellerRatings Merchant Reviews Scraper (`piquno/resellerratings-reviews-scraper`) Actor

Scrape the full review archive for any ResellerRatings merchant — past page 1, with incremental delta runs. HTTP-only, no browser, no proxy. 41 flat fields per review.

- **URL**: https://apify.com/piquno/resellerratings-reviews-scraper.md
- **Developed by:** [Piquno](https://apify.com/piquno) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ResellerRatings Merchant Reviews Scraper

Pull the **complete review archive** for any ResellerRatings merchant — not just the first page. HTTP-only, no browser, no proxy. 41 flat fields per review, ready for CSV or Excel.

**$1.50 per 1,000 reviews.** No charge for empty rows.

***

### Why this one

ResellerRatings is a React app. The review list is rendered client-side and **is not in the delivered HTML** — CSS selectors return zero records, which is why most attempts at this site produce an actor that runs cleanly and collects nothing.

This actor reads the embedded state blob the page ships with, so it gets all 15 reviews per page, every page, with fields the rendered page never shows.

It also handles the trap that quietly breaks the obvious implementation: **`?page=2` returns HTTP 200 and silently re-serves page 1.** Verified — 15 of 15 identical review ids. Only path pagination (`/store/{slug}/page/2`) actually advances. An actor built on the query form looks healthy in the log and collects the same page forever.

#### Built for repeat runs

Reviews are served newest-first, so `incremental` mode stops as soon as it reaches a review it has already seen. A scheduled run on a merchant with 24,000 reviews costs a page or two, not the whole archive.

| | This actor | Typical alternative |
|---|---|---|
| Reads past page 1 | Yes, to the merchant's real page count | Often page 1 only |
| Pagination | Path-based, verified disjoint | `?page=N` silently repeats |
| Repeat runs | Stops at first seen review | Re-bills full history |
| Empty rows | Never billed | Commonly billed |
| Filters | Applied **before** billing | Often applied after |
| Missing data | Stays `null` | Sometimes inferred |

***

### Input

```json
{
  "stores": ["TopMags", "Newegg"],
  "maxReviewsPerStore": 500,
  "onlyWithComment": true,
  "maxRating": 2
}
```

`stores` accepts a bare slug (`TopMags`) or a full URL (`https://www.resellerratings.com/store/TopMags`).

Setting `maxRating: 2` with no minimum gives you just the complaints — the most common reason people buy this data.

#### Monitoring a merchant

Set `incremental: true` and give each schedule its own `stateKey`. Only reviews the actor has not seen before are emitted, tagged `changeType: "new"`.

***

### Output

One flat row per review, with the merchant's rating context attached to every row.

```json
{
  "reviewId": "31928221",
  "storeName": "TopMags",
  "storeSlug": "TopMags",
  "storeWebsite": "https://www.topmags.com",
  "storeLifetimeRating": 4.73,
  "storeReviewCount": 5928,
  "starRating": 5,
  "comment": "Fast & Easy to subscribe - thank you!",
  "date": "2026-04-08T00:00:00.000Z",
  "userName": "Reviewer557527747",
  "badgeType": "Verified",
  "usefulVotes": 0,
  "merchantReplied": false,
  "reviewUrl": "https://www.resellerratings.com/store/TopMags/review/31928221",
  "fieldsPopulated": 26
}
```

#### Field fill rates

Measured over a live page. Published so you can judge completeness before you buy, not after.

| Field | Fill |
|---|---|
| `reviewId`, `comment`, `starRating`, `date`, `userName` | 100% |
| `title` | ~60% (many reviews have a body but no headline) |
| `city` | ~20% |
| `merchantReplyText` | only where the merchant actually replied |

`storePhone`, `storeEmail` and `storePostalCode` come from the page's structured-data block and are present only for merchants who published them.

***

### Pricing

| Event | Price |
|---|---|
| Review | **$0.0015** ($1.50 / 1,000) |
| Actor start | $0.00005 |

A row is billed only if it has a review id, and it is billed **before** it is pushed — so you are never charged for a row you did not receive, and never receive one you were not charged for. Rows removed by your filters are never billed. The actor honours the maximum charge per run you set in run options, and stops fetching once that budget is spent rather than burning compute it cannot bill.

***

### Limitations — read before you buy

- **Large merchants are deep.** 15 reviews per page means Newegg's 49,590 reviews are 3,306 requests. Use `maxReviewsPerStore`, date filters, or `incremental` rather than pulling everything in one run.
- **Star-only ratings exist.** Many reviews carry a rating and no text. `onlyWithComment` is on by default so you are not billed for them; turn it off if you want the full distribution.
- **Individual review permalinks are constructed, not fetched.** They are disallowed in robots.txt and unnecessary — every field is already on the store page.
- **Format risk.** Reviews are read from the page's embedded state blob. If the site changes its internals the actor stops rather than silently returning partial data, and the log says so explicitly.

***

### Running locally

```bash
npm install
npm test
```

Tests run against captured live pages in `test/fixtures/` — no network required. They assert the things that actually break this scraper: 15 records per page, page 2 disjoint from page 1, and newest-first ordering.

# Actor input Schema

## `stores` (type: `array`):

Store slugs (TopMags) or full URLs (https://www.resellerratings.com/store/TopMags). The slug is the last path segment of the store page.

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

Hard limit per merchant. 15 reviews are returned per page. Large merchants run deep — Newegg has 3,306 pages — so set this deliberately.

## `onlyWithComment` (type: `boolean`):

Skip star-only ratings with no review body. On by default because empty rows are rarely what anyone wants to pay for.

## `onlyMerchantReplied` (type: `boolean`):

Useful for studying how a merchant handles complaints.

## `incremental` (type: `boolean`):

Remember review ids between runs and emit only new ones. Reviews are served newest-first, so the actor stops as soon as it reaches a review it has already seen — a scheduled run costs a page or two instead of the whole archive.

## `stateKey` (type: `string`):

Namespace for incremental state. Use a different key per schedule so separate jobs do not share history.

## `minRating` (type: `integer`):

Only reviews at or above this rating (1-5).

## `maxRating` (type: `integer`):

Only reviews at or below this rating (1-5). Set to 2 with no minimum to pull just the complaints.

## `fromDate` (type: `string`):

ISO date, e.g. 2026-01-01.

## `toDate` (type: `string`):

ISO date, e.g. 2026-08-01.

## `proxyConfiguration` (type: `object`):

Optional and off by default. ResellerRatings served every probe from a plain address with no challenge, so the actor runs unproxied. Only enable this if you see repeated blocks in the log.

## Actor input object example

```json
{
  "stores": [
    "TopMags",
    "Newegg"
  ],
  "maxReviewsPerStore": 500,
  "onlyWithComment": true,
  "onlyMerchantReplied": false,
  "incremental": false,
  "stateKey": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per merchant review: rating, body, reviewer, merchant reply, plus store-level rating and volume.

## `runSummary` (type: `string`):

Rows pushed, reviews billed, rows filtered out, and reviews skipped as already seen.

# 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 = {
    "stores": [
        "TopMags"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("piquno/resellerratings-reviews-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 = { "stores": ["TopMags"] }

# Run the Actor and wait for it to finish
run = client.actor("piquno/resellerratings-reviews-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 '{
  "stores": [
    "TopMags"
  ]
}' |
apify call piquno/resellerratings-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,piquno/resellerratings-reviews-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/qB4thER9nARotaOZ9/builds/fQumwxQrrtbhZUEfU/openapi.json
