# Steam Review Mix Change Review (`remarkable_whipscorpion/steam-review-mix-change-review`) Actor

Compare the observed negative-vote share of supplied authorized Steam-shaped review records across two fixed seven-day UTC periods. Input only; aggregate output.

- **URL**: https://apify.com/remarkable_whipscorpion/steam-review-mix-change-review.md
- **Developed by:** [Kagerou AP](https://apify.com/remarkable_whipscorpion) (community)
- **Categories:** E-commerce, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.10 / useful provided review mix comparison saved

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?

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

## Steam Review Mix Change Review

Compare the observed vote mix in an authorized export of recommendation records from one Steam game. The Actor counts supplied positive and negative recommendations for 14 completed UTC days, then compares the first seven days with the last seven days. It outputs aggregates only.

The Actor does no Steam requests, scraping, API-key access or upstream Actor runs. Bring your own authorized records. Do not combine different games, snapshots or differently filtered populations in one input. A caller-declared label is not source verification.

### Try the original demo

```json
{"mode":"demo"}
```

This exact input is the Console prefill, API example and sample. The omitted-input default resolves to the same original synthetic fixture: 28 self-created recommendation-shaped records over September17–30,2026. The first period contains12 positive and2 negative votes; the recent period contains2 positive and12 negative votes. Negative share changes from2/14 to12/14, a rise of approximately71.428571 percentage points. The demo works independently of today's date and has no custom report event. Platform startup can still apply. Altered demo records, dates or labels are rejected; use review mode for custom data.

The fixture and custom example were created for this project, with no Steam records or review text. Their permission is described in LICENSE-DEMO.txt.

### Review supplied records

```json
{
  "mode": "review",
  "asOfDate": "2026-09-30",
  "dataRightsConfirmed": true,
  "sourceContext": "Caller-declared English-language export",
  "reviews": [
    {"recommendationid":"example-1","timestamp_created":1789646400,"voted_up":true}
  ]
}
```

The single-record illustration above is insufficient for a paid comparison. samples/custom-input.json contains the complete original28-record owned example for a useful custom run.

Input fields:

- `mode`: `demo` (default) or `review`.
- `reviews`: required explicit array in review mode, at most5000 records. Entire JSON input must fit1MiB measured as UTF-8. Each useful record needs nonempty string `recommendationid`, integer Unix UTC seconds `timestamp_created` from0 through253402300799, and boolean `voted_up`. IDs are exact strings; no casefolding, trimming, number conversion or aliases. Other record fields are ignored.
- `asOfDate`: required real `YYYY-MM-DD` in review mode. It is the inclusive final date, and its entire UTC day must already have elapsed. The window is the14 days from00:00:00 on date minus13 days through23:59:59 on the final date. Baseline is the first7; recent is the last7. Earliest allowed final date is1970-01-14. Current UTC day and future dates are rejected.
- `dataRightsConfirmed`: must be exactly`true` in review mode. Set it only when authorized to process the input. Authorization is caller-declared, not independently verified.
- `sourceContext`: optional single-line capture/filter label of1..200 characters. Include no reviewer identifiers, personal information or review text. It appears only as caller-declared and unverified. It does not establish capture time, filtering or completeness.

Unknown root fields are rejected. A typo cannot fall through to a paid default.

### What the report means

REPORT.json and the single default Dataset item contain the same packet. It includes14 UTC daily observed positive/negative counts, two fixed-period counts and nullable negative shares, signed recent-minus-baseline percentage-point difference, absolute difference and the20pp threshold. A comparison is useful only if each period has at least10 resolved observed reviews. A valid no-change result is useful. When either period is insufficient, thresholdMet is`null`, not a causal verdict.

Negative share is `observedNegative / (observedPositive + observedNegative)`, in0..1. Difference is `(recent share - baseline share) *100`, in percentage points. The20pp boundary uses exact integer cross multiplication; negative and positive changes both qualify by absolute size. A zero denominator produces`null` share and delta.

Exact duplicate IDs with equal timestamp and vote count once, even if ignored source fields differ. If one ID has conflicting timestamp or vote values, the entire input produces a nonuseful invalid diagnostic. Missing/unusable IDs are excluded and counted. Missing, fractional, out-of-range or nonnumeric timestamps are unresolved. Nonboolean votes remain unresolved, never converted to negative. Valid dated rows outside the window are excluded and counted. Daily unresolvedVoteRows include valid in-window timestamps with unknown votes. Timestamp/vote counters apply to distinct usable IDs; invalidIDRows counts excluded raw rows. Coverage counters overlap where a row has both invalid timestamp and invalid vote; duplicateRowsExcluded is a separate raw-row count.

Daily zeros mean zero resolved supplied observations for that day. The report keeps all-source day totals, capture time, filters and population completeness unknown. A filtered export cannot establish population-wide vote changes. The Actor does not infer review bombing, coordination, fraud, game quality, viral reach or causes. There is no review-text sentiment analysis, reviewer profiling or statistical significance claim.

No supplied recommendation ID, author field, review text or row-level source record enters the report, Dataset or checkpoint. The checkpoint retains only the aggregate packet and hashes. Caller-declared sourceContext is the sole optional input label copied to output.

### Price and output access

The proposed platform event `steam_review_mix_review_completed` costs$0.10 per useful custom report, at most one event per run. The startup event is$0.00005, separate from this custom event. Dataset item fee is0. Native configuration and billing acceptance require separate validation before publication.

Original demo, invalid input, insufficient/all-unknown data and unfunded diagnostics have0 custom events. A useful comparison that does not cross the20pp threshold still costs one event. Local non-PPE use is explicitly`local_unpriced`; this is not proof of platform billing.

Before any useful report facts enter Dataset, KVS or checkpoint, capacity must fund the full report event after startup and prior fees. Integer micro-USD arithmetic rejects subunit/fractional shortfalls. Budget0 or below-unit capacity returns a diagnostic with no useful day or period facts.

REPORT.json and Dataset are saved and read back identically before charging. Their immutable billing metadata is precharge`pending`; CHECKPOINT is the authoritative final receipt (`confirmed`, `no_custom_fee`, `local_unpriced` or `charge_unknown`). The packet does not pretend the fee was already confirmed when saved.

Same-run replay requires matching input/profile/report hashes. Completed replay verifies actual Dataset content and count. An absent REPORT can be restored from the exact bound checkpoint only when Dataset matches; a conflicting REPORT is never overwritten. Missing or mismatched Dataset fails honestly without append or another fee. A missing checkpoint with retained REPORT, Dataset or confirmed event count fails closed. Uncertain publication is reconciled only against the exact existing row. Unknown charge responses or a persisted charge-pending checkpoint are never retried blindly. Human/operator investigation may be needed to settle ambiguous billing, without rerunning the event.

### Minimal local development

Node24.22.1 and pinned Apify SDK3.7.2 are the sole runtime stack. The lock retains the prior dependency graph except for the targeted basic-ftp 6.2.1 security override and the root Node engine metadata. The shipping runtime has no CSV, HTTP, HTML parser or AI dependency.

`npm test` runs built-in Node tests for literal vote arithmetic, source exclusions, privacy, pricing boundaries and replay failure cases. `npm run sdk-test -- -RootSlotGranted -Case exact` is a separate bounded SDK harness; use that flag only after an actual resource slot is granted. Before importing the SDK, it requires an absolute SKU-only CRAWLEE_STORAGE_DIR and checks the actual resolved base. It never purges shared/default storage. SDK tests use a local charge endpoint stub and do not prove native settled ledger or clean Docker execution.

The Actor uses256MiB memory,120-second timeout and no Standby. There is no dashboard, account database, alerting, acquisition or automatic scheduling. Formal independent QA/Market and native Input/Dataset/Actor Output recognition remain release gates; source preparation alone does not establish those passes.

# Actor input Schema

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

demo uses only the original locked synthetic fixture; review requires your authorized records and explicit final completed UTC date.

## `reviews` (type: `array`):

For review mode, supply an explicit array of at most 5000 records. Fields: recommendationid (nonempty string, exact identity), timestamp_created (integer Unix UTC seconds), voted_up (boolean). Other source fields are ignored and never emitted. Whole input maximum 1 MiB. Do not mix games, snapshots or differently filtered populations. Empty or insufficient data has no custom fee.

## `asOfDate` (type: `string`):

Required in review mode: real YYYY-MM-DD date, inclusive final day. All 14 full UTC days ending on this date must have elapsed. Baseline is first7 days; recent is last7. Current UTC day and future dates are rejected. Earliest final date is 1970-01-14.

## `dataRightsConfirmed` (type: `boolean`):

Set true in review mode only if authorized to process these records. Missing/false rejects custom input. The original self-created demo is exempt.

## `sourceContext` (type: `string`):

Optional single-line label, 1..200 characters. Include no reviewer identifier, personal information or review text. It is copied as caller-declared and unverified; it never establishes capture time, filter completeness or population coverage. Absent means unknown.

## Actor input object example

```json
{
  "mode": "demo"
}
```

# Actor output Schema

## `report` (type: `string`):

Fourteen daily counts, two seven-day observed vote shares, signed percentage-point change and coverage. Same packet as Dataset. Precharge packet billing is pending; final fee receipt is CHECKPOINT.

## `dataset` (type: `string`):

Exactly one packet, identical to REPORT.json. No supplied review IDs, review text or reviewer fields are included.

## `billingReceipt` (type: `string`):

CHECKPOINT binds exact input and report hashes. billingState/eventCount is authoritative for confirmed, no_custom_fee, local_unpriced or charge_unknown. Uncertain charge is never retried automatically.

# 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 = {
    "mode": "demo"
};

// Run the Actor and wait for it to finish
const run = await client.actor("remarkable_whipscorpion/steam-review-mix-change-review").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 = { "mode": "demo" }

# Run the Actor and wait for it to finish
run = client.actor("remarkable_whipscorpion/steam-review-mix-change-review").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 '{
  "mode": "demo"
}' |
apify call remarkable_whipscorpion/steam-review-mix-change-review --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,remarkable_whipscorpion/steam-review-mix-change-review"
        }
    }
}
```

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/Ynw6LhNPKgruXvGB1/builds/m98uQascBEeVopggd/openapi.json
