# TikTok Shop Reviews Scraper & Change Monitor (US) (`jy-labs/tiktok-shop-us-reviews`) Actor

Monitor TikTok Shop US review changes. Pass a previous run ID to receive only newly observed or edited reviews, with the fields that changed. Unchanged reviews have no result charge. First run collects a baseline. One product, up to 99 recent reviews per run.

- **URL**: https://apify.com/jy-labs/tiktok-shop-us-reviews.md
- **Developed by:** [jy-labs](https://apify.com/jy-labs) (community)
- **Categories:** E-commerce, Social media, Automation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 review results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## TikTok Shop Reviews Scraper & Change Monitor (US)

**See what changed in your TikTok Shop US reviews.** Pass a previous successful run's ID to receive **only newly observed or edited reviews**, with the fields that changed. Use the results to spot rating changes, follow buyer feedback, or feed your own alerts without writing review-comparison logic.

Your first run collects a baseline. Later comparisons omit unchanged reviews and charge only for the review rows delivered. **Start and platform usage charges still apply, including when nothing changed.**

No login, no cookies, no proxy setup, no TikTok account of any kind.

> Unofficial. Not affiliated with, endorsed by, or operated by TikTok.

### See the difference in three runs

Illustrative example for one product with a 30-review window. These numbers explain the workflow; they are not a live result or a promise of how many reviews will change.

| Run | What you provide | What you receive | Review result charge |
| --- | --- | --- | --- |
| First collection | Product URL; leave previous run ID blank | 30 reviews, marked `baseline` | $0.090 |
| Next comparison | Same product and limit, plus the first run ID | 2 newly observed reviews + 1 edited review; 27 unchanged reviews omitted | $0.009 |
| Next comparison, nothing changed | Same settings, plus the second run ID | 0 rows; summary status `noChanges` | $0.000 |

The table excludes start and platform usage charges. An edited review is labelled `edited_observed`; if its rating changed, `changedFields` contains `["rating"]`. This identifies the changed field, not the previous rating value.

**Monitoring scope:** one US product and up to 99 recent reviews per run. "Newly observed" means absent from the previous checked window, not necessarily newly written. Deletions and edits outside that window are not detected. To keep monitoring, pass each successful run's ID into the next run; a schedule alone does not update it.

### Who uses this

- **Brand monitoring** — catch new reviews on your own listing the day they land, with star rating and verified-purchase status. You see the newest `maxReviews` reviews, so if more than that many arrive between two runs, the oldest of them fall outside the window.
- **Competitor tracking** — follow a rival product's feedback over weeks and read the drift, not the backlog.
- **Review-drop alerts** — schedule a daily run and fire a webhook only when the dataset is non-empty.
- **Dropshipping product research** — check what buyers actually say before you commit inventory.
- **Sentiment datasets** — build a dated corpus of review text and ratings for classification or model evaluation.
- **Agency reporting** — pipe a per-product review feed into a client dashboard without maintaining a scraper of your own.

### Quick start

1. Paste a product URL into **TikTok Shop US product URL**, for example `https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596`.
2. Set **Recent reviews to check (1-99)**. The default is 30. Leave **Track changes - previous run ID (optional)** blank for the first collection.
3. Click **Save & Run**, then open the **Dataset** tab.

That first run is your baseline: seeing `baseline` rows is expected. To activate change monitoring, open that successful run, copy its run ID, and paste it into **Track changes - previous run ID (optional)** next time:

```json
{
    "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596",
    "maxReviews": 30,
    "previousRunId": "PASTE_PREVIOUS_RUN_ID"
}
```

On that comparison run, the scraper delivers only newly observed or edited reviews. Open **Review changes** in the dataset and check **Change type** and **Changed fields**. Keep the same product and the same `maxReviews`, or the comparison is rejected rather than silently reset. The guard matches on product ID, so a differently written URL for the same product is fine.

**An empty result can be the expected answer.** Open **Change summary - including no changes** and check for `status: "noChanges"` with `comparison.mode: "comparison"`. This means the checked window has no tracked changes; an empty dataset by itself is not enough to tell. For the next comparison, use this successful run's ID even when it returned zero rows. See [Scheduled runs and webhooks](#integrations) to connect this loop to your own alerts.

### Sample TikTok Shop reviews output

One row per review. This is a real row from a run on the catnip chew toy above:

```json
{
    "deliveryId": "1777d8c9abfa6f306225e69893a1cc9157383434dd8a6862bc603f5061a0cb0a",
    "sourceUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596",
    "productId": "1731791261654290596",
    "productTitle": "Potaroma Interactive Catnip Chew Toy with Organic Sisal for Indoor Cats - TikTok Shop",
    "productTotalReviews": 3467,
    "productOverallRating": 4.6,
    "observedAt": "2026-09-08T08:34:21.599Z",
    "reviewId": "7682830511550514957",
    "text": "My Willow loves this",
    "rating": 5,
    "reviewedAt": "2026-09-07T16:26:18.000Z",
    "isVerifiedPurchase": true,
    "reviewCountry": "US",
    "changeType": "baseline",
    "changedFields": [],
    "firstSeenAt": "2026-09-08T08:34:21.599Z",
    "lastSeenAt": "2026-09-08T08:34:21.599Z",
    "requestedSort": "newest",
    "observedDescendingOrder": true,
    "fullReviewCoverage": false,
    "newestVerified": false,
    "browserQaPerformed": false
}
```

#### Fields

| Field | Example | What it means |
| --- | --- | --- |
| `reviewId` | `7682830511550514957` | Stable TikTok review ID. Use it as your primary key. |
| `text` | `My Willow loves this` | The review body. Empty when the buyer left stars only. |
| `rating` | `5` | Star rating, 1 to 5. |
| `reviewedAt` | `2026-09-07T16:26:18.000Z` | When the review was written, in UTC. |
| `isVerifiedPurchase` | `true` | TikTok's verified-purchase flag. |
| `reviewCountry` | `US` | Two-letter country shown on the review. |
| `productTitle` | `Potaroma Interactive Catnip Chew Toy…` | Product name as served on the page. `null` when no name was served. |
| `productTotalReviews` | `3467` | The product's total review count, not how many you received. `null` when the source served no total. |
| `productOverallRating` | `4.6` | The product's star average. `null` when the source served no rating. |
| `changeType` | `baseline` | Why this row was delivered. See below. |
| `changedFields` | `["rating"]` | On an edited review, which of text, rating, date, verified flag, or country changed. |
| `firstSeenAt` / `lastSeenAt` | `2026-09-08T08:34:21.599Z` | When your run chain first and last saw this review. |
| `observedAt` | `2026-09-08T08:34:21.599Z` | When this run started. |
| `sourceUrl`, `productId`, `deliveryId` | | The URL you asked for, TikTok's product ID, and a per-row delivery fingerprint. |
| `requestedSort` | `newest` | Always `newest`. Records what was asked of TikTok, not proof of what arrived. |
| `observedDescendingOrder` | `true` | Whether dates really came back newest-first. `null` when fewer than two reviews were selected. `false` only reaches you with `allowSourceOrderWarnings` on, because the default stops the run with `sourceOrderMismatch` instead. |
| `fullReviewCoverage` | `false` | Always `false`. A bounded window of recent reviews, never a complete archive. |
| `newestVerified` | `false` | Always `false`. Nothing proves these are globally the newest reviews. |
| `browserQaPerformed` | `false` | Always `false`. Pages load in a real browser, but nothing checks them visually. |

The last three are `false` in every run, by design, so no downstream system mistakes a recent-reviews window for a full review history. Three more fields appear only in special cases: `sourceOrderWarning`, `productAvailability`, and `productTitleSource`, all described below.

#### What `changeType` tells you

| Value | In plain words |
| --- | --- |
| `baseline` | Your first run for this product. Everything the scraper saw is delivered, so you have something to compare against. |
| `newly_observed` | New since your previous run. This review was not in the previous window. |
| `edited_observed` | Edited since your previous run. The review existed before, and its text, rating, date, verified flag, or country changed. `changedFields` names which. |

Reviewer names, user IDs, and profile pictures are never collected.

### Change monitoring

Run once with no previous run ID. Every review comes back as `baseline`. Keep that run's ID.

Pass it as `previousRunId` next time and you get only `newly_observed` and `edited_observed` rows. Unchanged reviews are neither delivered nor charged, and a run where nothing changed **succeeds with zero rows**, reported as `noChanges` in the run's `OUTPUT` record.

Chain runs by always passing the previous run's ID forward, and keep `maxReviews` fixed across the chain. Changing the product, the limit, or the account starts a different comparison, and the mismatched prior snapshot is rejected rather than silently accepted.

### Pricing

This scraper uses pay-per-event pricing:

- **$0.003 per review row delivered.** Baseline, new, and edited rows all count. Zero rows, zero review charges.
- **$0.00005 per Actor start, per GB.** Runs use 2 GB for the browser, so a start costs **$0.0001**.
- **Apify platform usage**, compute plus US residential proxy traffic, is metered by Apify and billed to your account on top: roughly **$0.02 per run**. Apify covers that usage for Free-plan accounts, so the platform column below applies to paid plans.

| Run | Result charges | Start charge | Platform usage | Total |
| --- | --- | --- | --- | --- |
| 30 reviews (default) | $0.090 | $0.0001 | ≈ $0.02 | ≈ **$0.11** |
| 99 reviews (maximum) | $0.297 | $0.0001 | ≈ $0.02 | ≈ **$0.32** |
| Nothing changed, or no reviews (0 rows) | $0.00 | $0.0001 | ≈ $0.02 | ≈ **$0.02** |

An empty run costs you only the start fee plus platform usage. That is what makes daily monitoring cheap: most days nothing changed, so most days you pay about two cents. A failed run also charges the start event and the platform usage it consumed, but never review rows.

### Integrations

**Apify API.** Two HTTP calls: start the run, then read the dataset. Keep the token in a header, not the URL.

```bash
curl --fail-with-body --request POST \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"productUrl":"https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596","maxReviews":30}' \
  "https://api.apify.com/v2/acts/jy-labs~tiktok-shop-us-reviews/runs?build=latest"
```

```bash
curl --fail-with-body --header "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json&clean=true"
```

**Scheduled runs and webhooks.** Schedule this scraper daily at, say, 07:00 UTC and attach a webhook on `ACTOR.RUN.SUCCEEDED`. Your endpoint reads the dataset, posts to Slack only when it is non-empty, and stores the finished run ID as the next `previousRunId`. That ID is the only state a monitoring loop keeps.

**n8n.** HTTP Request node to start the run, a poll loop until it is terminal, then read `OUTPUT` from the run's key-value store. Continue only when `OUTPUT.status` is `completed` or `noChanges`: route dataset rows for `completed`, and for `noChanges` skip processing but still advance the saved run ID.

**Make.** The same state machine with HTTP modules: start, poll, read `OUTPUT`, branch on status, iterate rows. Store the run ID only after the status check, so a failed run never becomes your baseline.

**Zapier.** Use the native Apify integration to trigger on a finished run and push review rows into Airtable, Notion, or an email digest. A zero-row run produces no items, which is the "nothing to report" signal.

**Google Sheets.** Export from the Dataset tab, or append rows through the Sheets API on a schedule. Write with `valueInputOption=RAW` and import ID columns as plain text, or long IDs get rounded into nonsense.

**Python and JavaScript clients.** Both official Apify clients drive this scraper in a few lines.

```python
from apify_client import ApifyClient

client = ApifyClient("APIFY_TOKEN")
run = client.actor("jy-labs/tiktok-shop-us-reviews").call(run_input={
    "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596",
    "maxReviews": 30,
})
for review in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(review["rating"], review["changeType"], review["text"])
```

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

const client = new ApifyClient({ token: 'APIFY_TOKEN' });
const run = await client.actor('jy-labs/tiktok-shop-us-reviews').call({
    productUrl: 'https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596',
    maxReviews: 30,
    previousRunId: 'PASTE_PREVIOUS_RUN_ID',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Inputs

| Input | Required | What it does |
| --- | --- | --- |
| `productUrl` | Yes | The TikTok Shop US product page URL, like `https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596`. Plain product link, no query string. |
| `maxReviews` | No | How many of the newest reviews to collect, 1 to 99. Default 30. |
| `previousRunId` | No | ID of an earlier successful run of this Actor. When set, you get only reviews that are new or edited since that run. Same product and same `maxReviews`. |
| `allowSourceOrderWarnings` | No | Default `false`. By default the run stops if TikTok returns a review out of date order, because the newest-first promise was broken. Turn it on to receive those reviews anyway, flagged with `sourceOrderWarning: true`. |

### What can go wrong

| Code | What happened | What to do |
| --- | --- | --- |
| `blocked` | TikTok showed a security challenge for this product instead of the page | Try again later. Some products are challenged far more often than others. |
| `apiStatus` | TikTok's reviews endpoint refused the request | Try again later. |
| `timeout` / `transport` | A slow or dropped connection through the proxy | Try again. The scraper already retries this on its own. |
| `wrongCountry` | The proxy exit was not in the United States | Try again; a new run gets new connections. |
| `deadlineExceeded` | The run ran out of its time budget | Lower `maxReviews` and try again. |
| `budgetExhausted` | Your run charge limit cannot cover the rows found | Raise the run's charge limit and start a new run. |
| `browserHostUnavailable` | No TikTok Shop page the run tried could load the reviews: every candidate was challenged, region-blocked, or unreachable | Retry later; contact support if it persists. |
| `browserSdkMissing` | A page loaded but never finished setting itself up, so the reviews request could not be made | Retry later; contact support if it persists. |
| `browserLaunchFailed` | The browser itself could not start for this run | Retry later; contact support if it persists. |
| `unsafeResume` | Something tried to restart a run that already produced output | Do not resurrect it. Start a fresh run. |

Every run writes a `RUN_DIAGNOSTICS` record naming the failing stage, a stable code, and what to do next. Send the run ID and the code to support, never proxy credentials or cookies.

### Special cases

**Products with no reviews.** The run succeeds with zero rows and reports the product's rating and total. That is a real answer, not an error.

**Star-only reviews.** Many buyers leave a rating and write nothing. Those rows have an empty `text` and are roughly half of a typical product's output. Real data, not a parsing failure.

**Region-blocked products.** Some products refuse to display in some regions. The scraper still delivers their reviews, read for that product ID from another TikTok Shop page. Because the product page served no name or totals, those rows carry `productAvailability: "regionBlocked"` and `productTitleSource: "slug"`: the title comes from the URL, and the total and star average from the reviews envelope. Treat that title as approximate, and expect `productTotalReviews` and `productOverallRating` to be `null` when that envelope carries no rating summary.

**Out-of-order reviews.** With `allowSourceOrderWarnings` on, rows whose source date broke the newest-first order carry `sourceOrderWarning: true` and the run summary counts them.

### Limits

- **US depth, not global breadth.** One product per run, US market only, up to 99 of the newest reviews. In exchange: the US verified-purchase flag, review country codes, and reviews even for products region-blocked from your own location.
- **A recent window, not an archive.** No paging back through a product's full history, and no guarantee this is globally the newest set. `fullReviewCoverage` and `newestVerified` say so on every row.
- **No reviewer identity**, by design: no names, user IDs, or avatars.
- **Deletions are not detected.** A review missing from a later run is not reported as removed.
- **The source can change without notice.** TikTok changed how review requests are signed on September 8, 2026, and this scraper was rebuilt around a real browser in response. Expect it to happen again.
- **Compliance is yours.** Not affiliated with TikTok. TikTok's [US Terms of Service](https://t.tiktok.com/legal/page/us/terms-of-service/en) require prior written approval for automated extraction, and as of September 9, 2026 that approval had not been established for this project. You remain responsible for your own compliance with applicable terms, rights, privacy obligations, and laws.

### FAQ

**What does one run cost?**

$0.003 per delivered review row, plus $0.0001 for the start at the default 2 GB. A 30-review run is about $0.09 in result charges, a full 99-review run about $0.30, with roughly $0.02 of platform usage on top.

**What does an empty run cost?**

The start event plus platform usage, about $0.02. No rows means no result charges, which is the economic case for scheduling it daily.

**How does change monitoring work?**

Run once with no previous run ID to get a baseline. Next run, pass that run's ID: the scraper compares the two windows and returns only new or edited reviews, each labelled with which. Unchanged reviews are neither delivered nor charged.

**Is it legal to scrape TikTok Shop reviews?**

Review text and ratings are public content, which is not the whole question. TikTok's US Terms of Service require prior written approval for automated extraction, and this project does not have it. Decide for your own jurisdiction and use case, and take legal advice if it matters commercially.

**Does TikTok Shop have a public reviews API?**

No. There is no open endpoint that serves product reviews to a third party, which is why this scraper reads the storefront in a real browser.

**Do I need cookies or a login?**

No. No TikTok account, no cookies, no session export, no proxy configuration. Paste a product URL and run it.

**What happens on a product with no reviews?**

The run completes with zero rows and reports the product's rating and review total. Zero rows on a product that demonstrably has reviews is treated as a failure, not an empty answer.

**Why do many rows have empty text?**

Because plenty of buyers tap five stars and write nothing. Roughly half the rows on a typical product are rating-only, and their rating, date, and verified-purchase flag are still there.

**Can I scrape several products in one run?**

No, one product per run. Start one run per product, in parallel if you like, keeping a separate `previousRunId` chain for each.

**How far back can it go?**

Up to 99 of the newest reviews per run. It is a recent-reviews window, with no deep pagination into a product's full history.

**Can it read products that are blocked in my region?**

Yes. When the product page itself is not served, the reviews are still collected and delivered. The title on those rows comes from the URL and is marked as such, so treat it as approximate.

**What about products behind a CAPTCHA?**

The run fails rather than returning partial data, and no result charges are made. A challenge is a refusal aimed at that session, not a fact about the product, so retrying later often succeeds.

**What happens if TikTok changes its site?**

Extraction can break, and it has. TikTok changed its request signing on September 8, 2026; the scraper moved into a real browser, keeping its fields, comparison semantics, and billing rules identical. When a change does break a run you get a diagnostic code, not silent bad data.

**Do I get the reviewer's name?**

No, deliberately. No names, user IDs, or profile pictures are collected, so the output carries review content without personal identifiers.

### Independent verification record

On September 8, 2026, output was compared field by field against a US browser session on a Los Angeles exit, across seven products. 39 reviews matched by `reviewId` with zero field mismatches, and product title, total, and star average matched exactly on every compared product. One product the browser session could not open returned no rows. That check ran against the earlier collection path; on September 9, 2026 the reviews stage moved into a real browser and region-blocked products began returning rows, while the delivered fields, comparison semantics, and billing rules stayed the same.

This is a dated one-off check against one exit location. It is not continuous monitoring, complete coverage, or an ongoing reliability claim.

# Actor input Schema

## `productUrl` (type: `string`):

The US product whose reviews you want to collect or monitor. Use the plain product link with no query string. For change monitoring, keep the same product across runs.

## `maxReviews` (type: `integer`):

Size of the recent-review window to check, default 30. A first run returns all reviews found in this window. A comparison returns only newly observed or edited reviews. Keep this number unchanged across the runs you compare. Reviews outside the window are not monitored.

## `previousRunId` (type: `string`):

Leave blank for your first collection. To get only newly observed or edited reviews, open an earlier successful run of this Actor, copy its run ID, and paste it here. Use the same account, product, and review limit. Changes identify the fields that changed; unchanged reviews are omitted. For repeated monitoring, pass each successful run's ID into the next run, even when it returned zero changes. Scheduling alone does not update this ID.

## `allowSourceOrderWarnings` (type: `boolean`):

By default the run stops if TikTok returns a review dated later than the one before it, because the newest-first promise was broken. Turn this on to receive those reviews anyway, flagged with sourceOrderWarning and counted in the run summary.

## Actor input object example

```json
{
  "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596",
  "maxReviews": 30,
  "allowSourceOrderWarnings": false
}
```

# Actor output Schema

## `reviewChanges` (type: `string`):

First run: collected reviews. With a previous run ID: only newly observed or edited reviews, including which fields changed.

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

Check status and counts. noChanges in comparison mode means no tracked changes were found in the checked review window. Zero rows alone do not distinguish no changes, an empty product, or failure.

## `runDiagnostics` (type: `string`):

Best-effort terminal status, failing stage, source attempts, HTTP counts, and support instructions. No raw source bodies or credentials.

# 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 = {
    "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596"
};

// Run the Actor and wait for it to finish
const run = await client.actor("jy-labs/tiktok-shop-us-reviews").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 = { "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596" }

# Run the Actor and wait for it to finish
run = client.actor("jy-labs/tiktok-shop-us-reviews").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 '{
  "productUrl": "https://shop.tiktok.com/us/pdp/catnip-chew-toy/1731791261654290596"
}' |
apify call jy-labs/tiktok-shop-us-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jy-labs/tiktok-shop-us-reviews"
        }
    }
}
```

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/fo2Rc3nOl0JLbV0sE/builds/EeeradLDs6wHF02lW/openapi.json
