# RedNote Creator Monitoring (`protocol/rednote-api`) Actor

RedNote Creator Monitoring turns Xiaohongshu (RedNote) into a scheduled, schema-versioned data feed for agencies and analytics teams tracking creator performance, not a hobby scraper.

- **URL**: https://apify.com/protocol/rednote-api.md
- **Developed by:** [Protocol](https://apify.com/protocol) (community)
- **Categories:** Social media, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $18.00 / 1,000 creator profiles

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

The unofficial RedNote / Xiaohongshu data API — extract creator profiles, the discovery feed, and post detail, and get computed within-sample intelligence signals (engagement quality, save-ratio, creator momentum) on every enriched row, billed only on success. Built for recurring creator monitoring: schedule a roster, dedupe seen notes, pay only for what's new.

Store name: **RedNote API** (for discoverability). This is the *RedNote Creator Monitoring* product — unofficial, not affiliated with or endorsed by RedNote/Xiaohongshu, and not an official API.

### Contents

- [Quick start](#quick-start) — run it in 60 seconds (JS / Python / curl)
- [Why this actor](#why-this-actor) — what's different
- [Built for](#built-for) — the ideal customer profile
- [Enterprise & reliability](#enterprise--reliability) — the production-grade guarantees
- [What it does](#what-it-does) — the three surfaces, honestly tiered
- [How it works](#how-it-works)
- [Input](#input) — full input contract
- [Output](#output) — row shape + the intelligence namespace
- [Pricing](#pricing) — Pay-Per-Event, from $18 / 1,000 profile rows
- [Recurring monitoring](#recurring-monitoring) — pay only for new notes
- [Use cases](#use-cases)
- [Integrations](#integrations) — Make, Zapier, n8n, Slack, Apify API/CLI
- [Honest scope & limits](#honest-scope--limits)
- [Comparison](#comparison) — vs other RedNote actors
- [FAQ](#faq)
- [Versioning & changelog](#versioning--changelog)
- [Support](#support)

### Quick start

Replace `<your-apify-username>~rednote-api` below with the Actor ID shown on the Apify Store page (the Actor is not yet pushed — this is the placeholder ID).

**Mode 2 — creator profile** (the recommended path; lead with this one):

```json
{
  "mode": "creator",
  "creatorUrls": [
    "https://www.xiaohongshu.com/user/profile/57206aec84edcd55224690c9"
  ],
  "dedupe": true,
  "maxItems": 100
}
```

**Mode 1 — search / discovery**:

```json
{
  "mode": "search",
  "query": "",
  "maxItems": 100
}
```

**Mode 3 — post / note detail** (Bounded Beta, cookie-only):

```json
{
  "mode": "post",
  "postUrl": "https://www.xiaohongshu.com/explore/6a278466000000001603fcad",
  "sessionCookie": "your-rednote-session-cookie-string"
}
```

**JavaScript (apify-client):**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<your-apify-username>~rednote-api').call({
  mode: 'creator',
  creatorUrls: ['https://www.xiaohongshu.com/user/profile/57206aec84edcd55224690c9'],
  dedupe: true,
  maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python:**

```python
from apify_client import ApifyClient

client = ApifyClient(token=os.environ['APIFY_TOKEN'])
run = client.actor('<your-apify-username>~rednote-api').call(run_input={
    'mode': 'creator',
    'creatorUrls': ['https://www.xiaohongshu.com/user/profile/57206aec84edcd55224690c9'],
    'dedupe': True,
    'maxItems': 100,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

**curl:**

```bash
curl -X POST "https://api.apify.com/v2/acts/<your-apify-username>~rednote-api/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"creator","creatorUrls":["https://www.xiaohongshu.com/user/profile/57206aec84edcd55224690c9"],"dedupe":true,"maxItems":100}'
```

### Why this actor

- **Within-sample intelligence signals on every enriched row** — a composite engagement-quality score plus the individual engagement, save, and comment ratios and normalized audience size, creator momentum, and a content fingerprint. `saveRatio` (collects/likes) is RedNote's distinctive save signal that no incumbent in this niche surfaces.
- **Recurring-monitoring dedupe — pay only for new notes** — schedule a roster of creators with `dedupe: true`; each run re-checks every profile but emits and bills only the new note cards since last time.
- **Schema-validated, versioned output** — every row validates against `output.v1` (`schemaVersion: "2.0.0"`) at the single write point, with the per-surface `data` shape enforced at the charge gate via runtime AJV. Invalid rows become structured error rows and are not charged.
- **PIPL-compliant by construction** — RedNote is China-hosted, so China's Personal Information Protection Law applies (not only GDPR). `ipLocation` and other PII are stripped by `src/sanitize.ts` at the write point, before any row is emitted.
- **Honest, bounded keyword-search depth** — we state openly that keyword-search depth is bounded (thin SSR — R-003); a deeper signed-API spike is roadmap, not V1. We tell you the ceiling rather than imply depth we cannot deliver.
- **No-charge-on-failure PPE** — Pay-Per-Event; you are charged only on a successful enriched result. Failed, partial, and `auth_required` results charge nothing.

### Built for

Primary ICP is agencies and social-intelligence analyst teams running recurring RedNote creator-monitoring pipelines, and developers integrating RedNote-derived data into broader analytics systems. The buyer need is structured, repeatable, diagnostics-rich access — not hobby tooling.

### Enterprise & reliability

The Actor is built to production-grade data-contract discipline. These are the guarantees that hold today — stated honestly, with no SLA claim beyond best-effort support.

- **Contract-stable, versioned output.** Every row validates against `output.v1` (`schemaVersion: "2.0.0"`) at the **single write point** (`src/normalizer.ts` — the sole writer), with the per-surface `data` shape enforced by **runtime AJV at the charge gate**. Additive fields (like the `intelligence` namespace) appear within `v1`; any breaking row-shape change ships as a new `output.v2` schema with a changelog migration notice — `v1` consumers are never broken silently.
- **Pay-Per-Event with no-charge-on-failure.** `Actor.charge()` fires **only** on a successful enriched result, **only** at the normalizer seam. Failed, partial, `auth_required`, `not_found`, `blocked`, and `error` rows are emitted as structured rows and cost nothing — cost is predictable, never a surprise.
- **Resilience ceilings, enforced in code.** ≤10,000 items/run · ≤500 pages/session · browser fallback ≤20% (structural at the adapter seam) · abort at 10% parse failures · ≤3 retries with backoff + jitter · 50 MB payload cap. The guardrails live in `src/limits.ts`, not just in docs.
- **PIPL-aware by construction.** RedNote is China-hosted, so China's Personal Information Protection Law applies (not only GDPR). `ipLocation` and other PII are stripped by `src/sanitize.ts` at the write point, before any row is emitted. Secret inputs (`sessionCookie`, `llmApiKey`) are encrypted at rest, never logged, never persisted, never written to output.
- **Deterministic, auditable intelligence.** Every Tier 1 intelligence sub-field is tagged `method: "deterministic"` / `scope: "within-sample"` — reproducible from the returned rows, no opaque model in the GA path. (Tier 3 LLM enrichment is opt-in, OFF by default, BYOK — never on by accident.)
- **Diagnostics-rich runs.** Every run emits a `RUN_SUMMARY` key-value record with per-status / per-surface counters, HTTP vs browser counts, retry / parse-failure / session-rotation counters, fallback ratio, item throughput, and outcome — so you can monitor pipeline health and feed downstream alerting.
- **Honest scope.** We state the ceiling openly rather than imply depth we cannot deliver (bounded keyword-search depth, R-003). Trend tracking, rising-creator detection, and historical backfill are deferred tiers — roadmap items, not commitments, and never claimed in this README.

> **No SLA** beyond best-effort support within 2 business days. Upstream access is unofficial and may drift. For volume or custom needs, reach out via the Apify Console actor contact.

### What it does

RedNote Creator Monitoring extracts publicly accessible, server-rendered RedNote / Xiaohongshu data across three surfaces, normalizes it, enriches it with deterministic within-sample intelligence, validates every row against a versioned schema, and bills you only for successful results.

- **Extracts** creator profiles (GA), the discovery / keyword search feed (GA), and post / note detail (Bounded Beta, cookie-only).
- **Normalizes** every Chinese-number metric (`4.4万` → `44000`, with the raw string and an `approx` flag preserved) — no `万`/`亿` math in your spreadsheet.
- **Enriches** each enriched `ok` row with a Tier 1 deterministic `intelligence` namespace — a composite engagement-quality score **plus its individual engagement, save, and comment ratios and normalized audience size**, creator momentum, and a content fingerprint — computed from the rows returned in this run only.
- **Validates** every row against `output.v1` (`schemaVersion: "2.0.0"`) at the single write point, with the per-surface `data` shape enforced at the charge gate (runtime AJV). Invalid rows become structured error rows and are not charged.
- **Bills per event** (Pay-Per-Event): you pay only on a successful enriched result — failed, partial, and `auth_required` results cost nothing.

Three extraction surfaces. The matrix below is honest about what works today, not aspirational — documented limits, never surprises.

| Mode | Surface | Anonymous | Cookie | Status |
|---|---|---|---|---|
| 2 | Creator profile | ✅ GA | — | **Primary.** Basic info + interactions + first-tab note cards (preview-only: title/cover/likes). Carries the Tier 1 intelligence namespace. |
| 1 | Search / discovery | ✅ GA `/explore` feed (empty `query`) | — | Keyword query returns thin SSR — **no note cards in V1** (R-003 / D-007). Feed cards do not carry the intelligence namespace. |
| 3 | Post / note detail | ❌ Not supported | ✅ Bounded Beta | Experimental, cookie-only, account-ban risk. Carries a partial Tier 1 intelligence namespace (fingerprint + null-valued EQS). |

- **Mode 2 — Creator profile (GA). The recommended path.** `/user/profile/<id>`
  — nickname, redId, bio, follower/fan/interaction counts (with
  `万`-normalized integers), profile tags, and the first-tab note cards. These
  cards are **preview-only** (title, cover, liked count, user) and do **not**
  carry `noteId`, `xsecToken`, or `noteUrl` — for deep-linkable note references
  use Mode 1 discovery cards. Anonymous, batchable (`creatorUrls`), and the
  least exposed to RedNote's login/signing arms race — which is why it leads.
  On a valid profile row the Actor attaches the **Tier 1 intelligence
  namespace** (see Output) and charges `intelligence_basic` exactly once.
- **Mode 1 — Search / discovery (GA).** Extracts the `/explore` discovery feed
  (~25–31 note cards per page, anonymous) — **only when `query` is empty**. A
  filled `query` hits `/search_result`, whose note cards load via RedNote's
  signed API; anonymous HTTP returns only a thin server-rendered state with **no
  note cards** (zero `ok` rows) — keyword card-extraction is the deferred
  signed-API spike, not V1 (R-003 / D-007). Leave `query` empty for cards. Feed
  rows aggregate cards from many creators and do **not** carry per-creator
  intelligence — within-sample intelligence is a per-creator contract, not a
  per-feed aggregate.
- **Mode 3 — Post / note detail (Bounded Beta — experimental).** `/explore/<noteId>`
  — full note detail, metrics, images, tags, hashtags. **Cookie-only**: requires
  your RedNote `sessionCookie`. Batch `postUrls` entries each need their own
  `xsec_token`; the single `postUrl` path is cookie-driven and does **not**
  require one. Carries **account-ban risk** (see below) and is **not the
  recommended path** — it's an experimental upsell, not a GA promise. Without a
  cookie it returns `auth_required` (a graceful structured result, not a run
  failure). Post rows attach a **partial** intelligence namespace:
  `contentFingerprint` plus an `engagementQualityScore` whose `value` is `null`
  (a single post exposes no follower count for the divide-by-zero guard), and
  the individual `engagementRate`/`saveRatio`/`commentRatio`/`i18nCount` ratios
  are `null` for the same reason; `creatorMomentum` is `null` (momentum needs
  ≥2 posts in the sample).

The bounded keyword-search depth (R-003) is a trust differentiator, not a softening of the honesty: we state the ceiling openly rather than imply a depth we cannot deliver.

### How it works

1. **Anonymous-first fetch.** The Actor fetches public, server-rendered RedNote pages over HTTP using **impit** (TLS + HTTP/2 browser fingerprinting), with a bounded Playwright browser fallback (≤20% of requests) only when a client-side challenge blocks the HTTP path. Mode 3 (post detail) is the one cookie-aware path.
2. **Resilient fetch.** Retry with exponential backoff + jitter (max 3 attempts), and the ≤20% browser-fallback ceiling enforced structurally at the adapter seam. Graceful degradation, never a silent break.
3. **Normalize → validate → enrich.** Each result is normalized to the `output.v1` shape, validated at the single write point (runtime AJV enforces the per-surface `data` `oneOf`), and — on `ok` — enriched with the Tier 1 `intelligence` namespace.
4. **Pay-Per-Event charge.** `Actor.charge()` fires only on a successful enriched result, only at the normalizer seam. Failed, partial, and `auth_required` results charge nothing.

### Input

Full contract: `.actor/input_schema.json`. `mode` is required; everything else is conditional on the mode.

| Input | Type | Default | Notes |
|---|---|---|---|
| `mode` | `search` | `creator` | `post` | `search` | Required. `creator` is the recommended path. |
| `creatorUrl` | string | — | Required for `mode=creator` (single profile). |
| `creatorUrls` | string\[] | `[]` | Batch alternative to `creatorUrl` — multiple profiles per run. Recommended for monitoring. |
| `query` | string | — | `mode=search`. **Empty = discovery feed (`/explore`) — returns ~25–31 note cards.** Filled = keyword search — **returns thin SSR, no note cards in V1** (signed-API spike deferred, R-003). |
| `postUrl` | string | — | Required for `mode=post` (single post, Bounded Beta, cookie-only). An `xsec_token` is **not** required on the single-post path — auth comes from `sessionCookie`. |
| `postUrls` | string\[] | `[]` | Batch alternative to `postUrl`. **Each entry must include its own `xsec_token`** — tokenless entries are rejected as malformed (canon 03:16). |
| `maxItems` | integer | `100` | 1–10,000. A hard **ceiling**, not a target — there is no pagination in V1, so discovery returns ~25–31 rows/run regardless. See [Limits](#limits). |
| `sort` | `relevance` | `recent` | `popular` | `relevance` | Keyword-search ordering hint. **Not yet wired into the outgoing request** (tracked as D-3) — accepted for forward compatibility only. |
| `region` | string | — | Bounded targeting hint, best-effort only. **Not yet wired** (D-3). |
| `language` | string | — | Bounded language hint. **Not yet wired** (D-3). |
| `sessionCookie` | string (secret) | — | Mode 3 only. Encrypted at rest; never logged, never persisted, never written to output. Using it may risk your RedNote account. |
| `proxyConfiguration` | object | — | Apify Proxy or custom proxy. |
| `includeDiagnostics` | boolean | `false` | Opt-in verbose run diagnostics in the run summary. |
| `dedupe` | boolean | `false` | Skip already-seen notes in subsequent runs — keyed by `noteId` for discovery cards and by a synthetic key (creator `userId` + title + cover) for creator-tab cards, which don't carry a `noteId`. Persistent seen-set in the KV store. Recommended for scheduled/recurring runs. |
| `outputTransform` | object | — | Optional (D-031). Reshape each row's `data` — `fields` (dot-paths to keep), `flatten` (collapse nested objects to dot keys), `classify` (group the run's rows by dot-paths into per-class counts in the run summary; does not alter rows). Applied per-row AFTER billing fires on the original; the row re-validates against output.v1 and reverts on failure. Emits rows on the additive `surface: "transformed"` branch. GA, not Tier 3, not behind a flag. |

**Proxy / CN-egress note.** RedNote is China-hosted. Residential CN-egress proxies via `proxyConfiguration` improve Mode 1/2 reliability; for Mode 3 use a proxy matching the cookie's origin account. `sort`, `region`, and `language` are accepted for forward compatibility but are not yet wired into the outgoing request (tracked as D-3).

### Output

Every dataset row is validated against
[`schemas/output.v1.schema.json`](schemas/output.v1.schema.json) at the single
write point (`src/normalizer.ts`). Each row carries a `surface`, a
`requestStatus`, a `confidence` tier, a `fetchedAt` timestamp, a `data` payload
(`null` for non-`ok` statuses), and — when Tier 1 enrichment succeeded — an
additive `intelligence` namespace. A row **without** the `intelligence`
namespace still validates: the namespace is additive, not required.

**Top-level row fields:**

| Field | Type | Notes |
|---|---|---|
| `schemaVersion` | `"2.0.0"` | The contract version. |
| `surface` | `search` | `creator` | `post` | `insight` | `transformed` | Which mode produced the row. `insight` is an internal prototype, not emitted in GA runs. `transformed` is emitted via the `outputTransform` input. |
| `requestStatus` | `ok` | `partial` | `auth_required` | `not_found` | `blocked` | `error` | An `auth_required` row (Mode 3 with no cookie) is a normal structured result with `data: null`, not a run failure. |
| `url` | string | The source URL. |
| `data` | object | null | Normalized payload; `null` for non-`ok` statuses. |
| `confidence` | `stable` | `best_effort` | `experimental` | See tiers below. |
| `fetchedAt` | string (ISO 8601) | When the row was written. |
| `intelligence` | object (optional) | Tier 1 deterministic intelligence — present only on `ok` rows whose enrichment succeeded. Additive; omitted on search rows and on any row where enrichment degraded. |

**Confidence tiers:**

- `stable` — name, type, and nullability protected across minor releases.
- `best_effort` — present and documented, but values may be absent or approximate.
- `experimental` — may change or disappear with limited notice.

**`intelligence` namespace** (Tier 1 deterministic, within-sample — additive,
schema-validated, billed as `intelligence_basic` once per enriched row). The
three composite sub-fields are **required** when the namespace is present; the
four individual-ratio sub-fields (D-030) are **additive optional** and always
populated on an enriched row (`value: null` on the post surface, which exposes
no follower count):

| Sub-field | Type | Notes |
|---|---|---|
| `engagementQualityScore` | object | null | **Composite** 0–1 = mean of `engagementRate` + `saveRatio` + `commentRatio`. `null` on divide-by-zero (zero followers or zero likes — never `NaN`). For a single post (Mode 3) the `value` is `null` because a post exposes no follower count. |
| `engagementRate` | object | null | **D-030.** `(likes + collects + comments) / followers`, clamped to \[0,1] (engagement can exceed the audience). The individual signal behind the composite. `null` on the same divide-by-zero guard. |
| `saveRatio` | object | null | **D-030.** `collects / likes` in \[0,1] — RedNote's distinctive save signal; no incumbent surfaces it. `null` when likes are zero; `0` when the surface carries no collects (creator-tab cards are preview-only). |
| `commentRatio` | object | null | **D-030.** `comments / likes` in \[0,1]. `null` when likes are zero; `0` when the surface carries no comments (creator-tab cards). |
| `i18nCount` | object | null | **D-030.** The i18n-normalized follower count — the denominator of `engagementRate`, surfacing the `万`/`亿` parse as a first-class signal. `null` on the post surface (no follower count). |
| `creatorMomentum` | object | null | Within-sample rolling-average engagement, post cadence (days between consecutive posts), and most-recent-vs-median delta. `null` when fewer than 2 posts are in the returned sample. |
| `contentFingerprint` | object | Hashtags + topics + `noteType` + `language` (`zh`/`en`/`null`) + `hasVideo` + `imageCount`. Extracted from whatever the surface's `data` carries. |

Every sub-field carries the frozen tags `method: "deterministic"` and
`scope: "within-sample"`. "Within-sample" means the signals are computed from
the rows returned in this run only — they are **not** longitudinal trends and
**not** cross-sample comparisons. We return signals, not just rows; we do not
claim trends, rising-creator detection, or historical access (those tiers are
deferred — see Known limitations).

#### Sample dataset rows

One real `output.v1` row for the primary surface (values drawn from the
project's sanitized fixtures). Metrics show the raw string, the parsed integer,
and an `approx` flag side by side.

**Mode 2 — creator profile** (`surface: "creator"`, billed `creator_profile`
plus `intelligence_basic`; note cards bill `creator_note` and do not carry the
intelligence namespace):

```json
{
  "schemaVersion": "2.0.0",
  "surface": "creator",
  "requestStatus": "ok",
  "url": "https://www.xiaohongshu.com/user/profile/57206aec84edcd55224690c9",
  "confidence": "best_effort",
  "fetchedAt": "2026-06-30T09:12:04.000Z",
  "data": {
    "basicInfo": {
      "redId": "622954348",
      "nickname": "和女儿跳舞的波斯猫妞",
      "desc": "女儿已窈窕 妈妈还未老 时光正正好…",
      "gender": 1,
      "ipLocation": "",
      "images": "https://sns-avatar-qc.xhscdn.com/avatar/61eb8adda1b9bac71f61f7a5.jpg?imageView2/2/w/360/format/webp",
      "imageb": "https://sns-avatar-qc.xhscdn.com/avatar/61eb8adda1b9bac71f61f7a5.jpg?imageView2/2/w/540/format/webp"
    },
    "interactions": [
      { "type": "follows",     "name": "关注",      "count": "10+",  "countParsed": 10,    "countApprox": true, "i18nCount": "10+" },
      { "type": "fans",        "name": "粉丝",      "count": "1万+", "countParsed": 10000, "countApprox": true, "i18nCount": "10K+" },
      { "type": "interaction", "name": "获赞与收藏", "count": "1万+", "countParsed": 10000, "countApprox": true, "i18nCount": "10K+" }
    ],
    "tags": [
      { "name": "",        "tagType": "info" },
      { "name": "舞蹈博主", "tagType": "profession" }
    ],
    "notes": [
      {
        "displayTitle": "都说妈妈看着年轻，那是因为你们没见过爸爸",
        "type": "video",
        "user": {
          "userId": "57206aec84edcd55224690c9",
          "nickname": "和女儿跳舞的波斯猫妞",
          "avatar": "https://sns-avatar-qc.xhscdn.com/avatar/61eb8adda1b9bac71f61f7a5.jpg"
        },
        "cover": { "url": "http://sns-webpic-qc.xhscdn.com/…!nc_n_nwebp_mw_1", "width": 596, "height": 796 },
        "likedCount": "2.3万",
        "likedCountParsed": 23000,
        "likedCountApprox": true
      }
    ]
  },
  "intelligence": {
    "engagementQualityScore": { "value": 0.33, "method": "deterministic", "scope": "within-sample" },
    "engagementRate":         { "value": 1.0,  "method": "deterministic", "scope": "within-sample" },
    "saveRatio":              { "value": 0,    "method": "deterministic", "scope": "within-sample" },
    "commentRatio":           { "value": 0,    "method": "deterministic", "scope": "within-sample" },
    "i18nCount":              { "value": 10000, "method": "deterministic", "scope": "within-sample" },
    "creatorMomentum": {
      "rollingAvgEngagement": 3055,
      "postCadenceDays": null,
      "recentVsMedianDelta": null,
      "method": "deterministic",
      "scope": "within-sample"
    },
    "contentFingerprint": {
      "hashtags": [],
      "topics": ["舞蹈博主"],
      "noteType": "video",
      "language": "zh",
      "hasVideo": true,
      "imageCount": 0,
      "method": "deterministic",
      "scope": "within-sample"
    }
  }
}
```

> Creator-tab note cards are **preview-only** (title, cover, liked count, user).
> Probe-verified 2026-07-02: `noteId`, `xsecToken`, and `noteUrl` are all absent
> on this surface — only `displayTitle`, `type`, `user{userId,nickname,avatar}`,
> `cover{url,width,height}`, and `likedCount` are returned. `publishedAt` is also
> empty, so `creatorMomentum.postCadenceDays` is `null` for a profile whose
> sample has unparseable timestamps. The individual ratios in the sample above
> reflect this: `engagementRate` is clamped to `1.0` (23,000 likes against
> 10,000 followers), while `saveRatio` and `commentRatio` are `0` because
> creator-tab cards carry no collects or comments.
>
> The `notes[]` array is shown truncated to **1 of the 31 notes** the profile
> actually returned in this run — the pipeline's `explodeEntity` unwraps each
> note to the FLAT `noteCard` shape (the parser wrapper
> `{noteId, xsecToken, noteUrl, noteCard, publishedAt}` is dropped before emit;
> an empty `publishedAt` is not merged in). `creatorMomentum.rollingAvgEngagement`
> is the aggregate mean across all 31 notes (~94,706 total likes ÷ 31 ≈ 3055),
> NOT the single displayed note's 23,000 likes. `recentVsMedianDelta` is `null`
> (not `0`) because every `publishedAt` is empty, so the `dated[]` array used by
> the most-recent-vs-median computation is empty. For deep-linkable note
> references (`noteId` / `xsecToken` / `noteUrl`), use Mode 1 discovery cards.
> Mode 3 (post detail) is entered via a Mode-1 `noteId` + `xsecToken`, or via a
> user-supplied `postUrl` (the single-post path is cookie-driven and does not
> require an `xsec_token`; batch `postUrls` entries each do) — not via a
> creator-tab card.

Search and post sample rows: see `fixtures/` and `schemas/output.v1.schema.json`.

> `ipLocation` is **stripped** from output under PIPL (canon 08 — it is on the
> PII deny list and `sanitize()` removes it before any row is emitted). The
> field above is illustrative of the upstream payload only; it never reaches
> the dataset.

### Pricing

**from $18 / 1,000 enriched creator-profile rows.**

Worked-example cost for a daily monitoring run: daily monitoring of 20 creators returning 12 new notes ≈ 20× `creator_profile` ($0.36) + 12× `creator_note` ($0.072) + 1× `scheduled_run` ($0.02) = ~$0.45/run, with `intelligence_basic` at the Apify platform-minimum $0.01/1,000 on the enriched rows (≈$0.0003 here — negligible, so the total stays ~$0.45). At daily cadence ≈ $13.50/month.

**Pay-Per-Event.** You are charged only on a successful enriched result;
failed, partial, and `auth_required` results cost nothing. Five priced events
plus Tier 1 intelligence, which is bundled at no surcharge (Apify platform minimum $0.01/1,000):

| Event | Price (USD) | When it fires |
|---|---|---|
| `discovery_result` | $0.010 | One enriched search/discovery result returned (Mode 1). |
| `creator_profile` | $0.018 | One enriched creator profile returned (Mode 2, profile row). |
| `creator_note` | $0.006 | One enriched creator note card returned (Mode 2, notes-tab row, preview-only). |
| `post_detail` | $0.025 | One enriched post-detail record returned (Mode 3, Bounded Beta). |
| `scheduled_run` | $0.02 (flat, per scheduled run) | A completed scheduled analytics run — **additive**, on top of the per-result charges. |
| `intelligence_basic` | **$0.01 / 1,000 (platform minimum — effectively no surcharge)** | Tier 1 deterministic intelligence (EQS + creator momentum + content fingerprint) attached to a billable row. Fires exactly once per enriched `ok` row, only when enrichment succeeded. Failed/partial/error rows charge nothing. Apify rejects a literal $0, so the price is the platform floor — it bills as an auditable usage signal, not a real cost. |

Launch prices for the first five events were set 2026-07-04 (matching
`src/billing.ts`), priced at the incumbent comparable Actor — not above; the
honesty, governance, and data-quality is the premium, not the sticker.

**Tier 1 intelligence is bundled at no surcharge.** `intelligence_basic` is
priced at the Apify platform minimum ($0.01/1,000; Apify rejects a literal $0):
it is deterministic arithmetic over data you already paid for via the surface
event, so there is no marginal cost to pass on — effectively free. The event
still fires on every enriched row, so it stays visible in your charge log as an
auditable usage signal.

**Scheduled-run charge is additive.** Scheduled runs incur a per-run
`scheduled_run` charge **in addition to** the per-result charges above. A
scheduled monitoring run that returns 12 new creator notes bills 12
`creator_note` events (plus one `creator_profile` per profile row, plus any
`intelligence_basic` on enriched rows) plus one `scheduled_run` event. Any
pricing change ships with at least 14 days' notice — never a surprise.

### Recurring monitoring

Track a roster of creators and only pay for new notes (this is the workflow behind the worked-example cost above):

1. **Configure a run** with `mode: "creator"` and your roster in `creatorUrls`
   (batch multiple profiles per run).
2. **Set `dedupe: true`.** The Actor persists a seen-set of dedupe keys in the
   key-value store and skips anything it has already returned — keyed by
   `noteId` for discovery cards and by a synthetic key (creator `userId` +
   title + cover) for creator-tab cards, which don't carry a `noteId`.
3. **Save it as a Task and put it on a schedule** in the Apify Console (e.g.
   daily or weekly). Each scheduled run re-checks every profile but emits —
   and bills — only the *new* note cards since last time. The profile row
   itself (basic info + intelligence) is re-emitted each run so you can track
   within-sample momentum on the latest sample.
4. **Read the deltas.** Each run's dataset is the new-notes report; the
   `RUN_SUMMARY` key-value record carries run-level counts and outcome.

### Use cases

- **Recurring creator & competitor monitoring.** Schedule `mode: "creator"` over a roster of profiles with `dedupe: true` and pay only for *new* note cards each run — the headline workflow (see [Recurring monitoring](#recurring-monitoring)).
- **New-content alerting.** The dedupe seen-set turns each scheduled run into a "what's new since last time" report.
- **Within-sample engagement quality.** Score a creator's returned notes by engagement quality (0–1) and momentum to prioritize outreach within the sample this run returned.
- **Content fingerprinting & tagging.** Hashtags, topics, note type, language, and media shape on every enriched row — for filtering, dedupe, or feed classification.
- **Discovery feed browsing.** Pull the anonymous `/explore` feed (~25–31 cards per page) for content discovery and deep-linkable note references.
- **Post / note detail lookup.** Resolve a single note's full detail, metrics, and tags (Mode 3, cookie-only, Bounded Beta).

### Integrations

Standard Apify integrations work out of the box — the Actor is callable via the **Apify API** (JavaScript, Python, CLI; see [Quick start](#quick-start)) and connects to the usual automation targets:

- **Make / Zapier / n8n** — trigger a run on a schedule or event, then route the dataset to your destinations (Sheets, Airtable, Slack, Notion, a webhook).
- **Slack / email / webhook** — use the `RUN_SUMMARY` key-value record (per-status counts, outcome, item throughput) to drive "new notes since last run" alerts.
- **Apify Scheduler** — save a `mode: "creator"` + `dedupe: true` config as a Task and schedule it daily/weekly for recurring monitoring (see [Recurring monitoring](#recurring-monitoring)).
- **Apify API / CLI** — `apify call`, `apify-client` (JS), `apify-client` (Python) — programmatic access for your own pipelines.

Output is plain validated JSON in the Apify dataset — pull it with any tool that reads the Apify dataset API.

### Honest scope & limits

> **No pagination (V1).** The Actor fetches one page per URL. Discovery mode
> therefore returns roughly one feed page (~25–31 cards) per run no matter how
> high `maxItems` is set; scale comes from supplying more URLs (creator batches),
> not from deeper paging. See [Limits](#limits).
>
> **Not supported (roadmap, not commitments):** longitudinal trend tracking, rising-creator detection, historical backfill beyond bounded windows, demographic insights, brand-sentiment analysis, creator-fit scoring, cross-platform comparison, and any MCP server integration. These are deferred tiers — see [Known limitations](#known-limitations).

- **Anonymous access depends on RedNote's current server-side rendering.** The
  GA (anonymous) modes work because RedNote server-renders public profile and
  discovery state today. **If RedNote tightens login-gating, GA modes may
  degrade to `auth_required` with notice** (a structured result, not a silent
  break) — we monitor SSR completeness as an operational signal and will
  announce any degradation in the changelog.
- **PIPL applies.** RedNote / Xiaohongshu is China-hosted, so China's Personal
  Information Protection Law is the applicable privacy regime (not only GDPR).
  `ipLocation` and other PII are stripped by `src/sanitize.ts` before any row
  is emitted.
- The dataset may contain personal information from public profiles. **You are
  responsible** for lawful use and any redistribution of that data.
- **Mode 3 carries account-ban risk.** Using your own RedNote session for
  automated extraction may put your RedNote account at risk of restriction or
  ban. Use a throwaway or expendable account, and use this mode at your own
  risk. The Actor does **not** scrape or generate cookies — you bring your own
  from an authenticated browser session (DevTools → Application → Cookies →
  `xiaohongshu.com`, copy `name=value` pairs separated by `; `).
- **No SLA.** Upstream access is unofficial and may drift; availability and
  field coverage are best-effort. Support is best-effort **within 2 business
  days**.

#### Limits

Per-run ceilings (enforced in `src/limits.ts`):

> **No pagination in V1 — one page fetched per URL.** These are *ceilings*, not
> reachable targets. The Actor fetches a single page per URL and does not
> paginate, so the rows a run can actually return is driven by how many URLs you
> supply, not by `maxItems`:
>
> | Mode | Realistic rows per run |
> |---|---|
> | Search / discovery | **~25–31** — one `/explore` page, regardless of `maxItems` |
> | Creator | first-tab note cards per profile × the number of `creatorUrls` |
> | Post | 1 row per URL (≤500 URLs) |
>
> Only creator batches approach the 10,000 ceiling. Deeper depth needs the
> signed-API spike tracked as R-003 (roadmap, not V1).

| Ceiling | Value |
|---|---|
| Max items per run | 10,000 (ceiling — see the pagination note above) |
| Max pages per session | 500 |
| Max browser-fallback ratio | 20% |
| Parse-failure abort ratio | 10% |
| Max payload per run | 50 MB |
| Max retries per request | 3 |

#### Known limitations

- **Keyword-search depth is bounded** (see Mode 1) pending a deeper signed-API
  spike.
- **Creator-note dedupe uses a synthetic key.** Note cards on a creator's
  profile tab carry no `noteId`, so recurring-monitoring dedupe keys them on
  `userId + displayTitle + cover.url`. Two distinct notes from the same creator
  that share all three (e.g. a repost or re-upload of the same cover + title)
  would collide and the second would be silently deduped. Discovery cards
  (Mode 1) are unaffected — they carry a real `noteId`.
- **Within-sample intelligence is not longitudinal.** `creatorMomentum` and
  `engagementQualityScore` are computed from the rows returned in this run only.
  The following are **deferred (candidate, not GA)** and are not claimed by
  this Actor: trend tracking, rising-creator detection, historical backfill
  beyond bounded windows, demographic insights, brand-sentiment analysis,
  creator-fit scoring, cross-platform comparison, and any MCP server
  integration. They are roadmap items, not commitments.

### Comparison

Capability-by-capability against the other RedNote / Xiaohongshu Actors in the Apify Store. Competitor data from public Apify Store pages, 2026-08-04; verify before relying.

| Capability | This Actor (RedNote API) | sian.agency | zen-studio | habit.zhou |
|---|---|---|---|---|
| Keyword search | ✅ (bounded depth, R-003) | ✅ (searchUser) | ✅ (deep, ~7,000/keyword) | ✅ |
| Creator profile | ✅ GA (primary) | ✅ | ❌ (search-only) | ✅ |
| Post / note detail | ✅ Bounded Beta (cookie-only) | ✅ ($0.020/note-detail) | ❌ | ✅ |
| Comments | ❌ Not in V1 (deferred) | ✅ (noteComments) | ❌ | — |
| Within-sample intelligence signals (EQS / saveRatio / momentum / fingerprint) | ✅ UNIQUE | ❌ | ❌ | ❌ |
| Recurring-monitoring dedupe (pay only for new notes) | ✅ UNIQUE | ❌ | ❌ | ❌ |
| Schema versioning (output.v1, runtime AJV) | ✅ UNIQUE | ❌ | ❌ | ❌ |
| PIPL ipLocation stripping at write point | ✅ UNIQUE | ❌ | ❌ | ❌ (returns author IP location) |
| No-charge-on-failure PPE | ✅ | ❌ (run-start fee) | ❌ | ❌ |
| Trending / KOL discovery | ❌ Not claimed (deferred) | ❌ | ❌ | ✅ (claims it) |
| Login required (Mode 1/2) | ❌ anonymous | ❌ | ❌ | ❌ |

### FAQ

**How much does it cost?** — Pay-Per-Event; charged only on a successful enriched result. Five priced events (`discovery_result` $0.010, `creator_profile` $0.018, `creator_note` $0.006, `post_detail` $0.025, `scheduled_run` $0.02 flat/run) plus `intelligence_basic`, which is **bundled at no surcharge** ($0.01/1,000 — the Apify platform minimum, effectively free; Tier 1 is deterministic arithmetic over data the surface event already paid for). Failed/partial/`auth_required` rows charge nothing. See [Pricing](#pricing) for a worked daily-monitoring cost example.

**Is scraping Xiaohongshu free?** — The Apify free tier ($5 platform credit) lets you try the Actor at no cost — roughly ~275 enriched creator-profile rows ($0.018 each) or ~500 discovery results ($0.010 each) before paid billing, with `intelligence_basic` at the platform-minimum $0.01/1,000 (effectively no surcharge — Tier 1 is deterministic arithmetic over data the surface event already paid for). Pricing limits surface as a clear upgrade message, never a bug-like error.

**Do I need a cookie or login?** — Mode 1 (search) and Mode 2 (creator) are anonymous: no cookie, no login, no account-ban risk to you. Mode 3 (post detail) is Bounded Beta and cookie-only: it requires your RedNote `sessionCookie` and carries account-ban risk. We do not scrape or generate cookies — you bring your own from an authenticated browser session (DevTools → Application → Cookies → xiaohongshu.com).

**Is Mode 3 safe for my account?** — No. Mode 3 uses your own RedNote session for automated extraction and may put your account at risk of restriction or ban. Use a throwaway/expendable account, use a proxy matching that account's origin, and use Mode 3 at your own risk. It is Bounded Beta with no SLA. Modes 1 and 2 carry no such risk.

**How do I get creatorUrls, postUrls, or xsec\_token?** — `creatorUrl` is any `xiaohongshu.com/user/profile/<id>` URL. `postUrl` is any `xiaohongshu.com/explore/<noteId>` URL. For batch `postUrls`, each entry must include its own `xsec_token` (from the discovery feed's `noteUrl` query string); the single `postUrl` path is cookie-driven and does not require one. Tokenless batch entries are rejected as malformed.

**What does "within-sample" intelligence mean?** — The intelligence signals (`engagementQualityScore`, `engagementRate`, `saveRatio`, `commentRatio`, `i18nCount`, `creatorMomentum`, `contentFingerprint`) are computed from the rows returned in THIS run only. They are NOT longitudinal trends, NOT cross-sample comparisons, and NOT rising-creator detection. Every sub-field is tagged `method: "deterministic"`, `scope: "within-sample"`.

**Do you fetch comments?** — Not in V1. Comments retrieval is a candidate/deferred capability per our canon (not a GA feature). If you need note comments today, sian.agency's xiaohongshu scraper offers a `noteComments` operation; we do not, and we say so rather than imply it.

**How do I schedule recurring monitoring?** — Configure `mode: "creator"` with your roster in `creatorUrls` and `dedupe: true`, save it as an Apify Task, and put it on a daily/weekly schedule in the Apify Console. Each scheduled run re-checks every profile but emits and bills only NEW note cards since last time; the profile row (with intelligence) is re-emitted each run. A flat `scheduled_run` charge applies per scheduled run, additive to per-result charges.

**Can I use integrations (Make, Zapier, Slack, n8n) and the Apify API?** — Yes, via standard Apify integrations and the Apify API (JS, Python, CLI). See [Quick start](#quick-start) for copy-paste snippets.

**Can I use it through an MCP server?** — Not yet. Any MCP server integration is deferred (roadmap, not a commitment). The Actor itself is callable via the Apify API and integrations.

**Is it legal to scrape Xiaohongshu?** — This Actor extracts publicly accessible, server-rendered data. It is unofficial and not affiliated with or endorsed by RedNote/Xiaohongshu. RedNote is China-hosted, so China's PIPL applies (we strip `ipLocation` and PII at the write point). You are responsible for lawful use and any redistribution of the dataset. This is not legal advice.

**What are the limits?** — ≤10,000 items/run, ≤500 pages/session, browser fallback ≤20%, abort at 10% parse failures, ≤3 retries, 50MB payload cap. **These are ceilings, not targets: there is no pagination in V1** — the Actor fetches one page per URL, so discovery returns ~25–31 rows/run regardless of `maxItems`, and scale comes from supplying more URLs (creator batches). Keyword-search depth is bounded (we tell you the ceiling; a deeper signed-API spike is roadmap, not V1). Creator-note dedupe uses a synthetic key (`userId` + title + cover) because creator-tab cards carry no `noteId`.

**What about CN-egress proxies?** — RedNote is China-hosted. Residential CN-egress proxies via `proxyConfiguration` improve Mode 1/2 reliability. For Mode 3, use a proxy matching the cookie's origin account.

**Something is not working / feedback** — Best-effort support within 2 business days; no SLA beyond this. Upstream access is unofficial and may drift; field coverage is best-effort.

### Versioning & changelog

**Output versioning.** `output.v1` (`schemaVersion: "2.0.0"`) is the contract. Additive, backward-compatible fields (such as the `intelligence` namespace) may appear within `v1`. Any **breaking** change to row shape ships as a new `output.v2` schema, announced in the changelog with migration notice — `v1` consumers are never broken silently.

See [CHANGELOG.md](CHANGELOG.md) for version history and migration notes.

### Support

Best-effort support **within 2 business days**. There is no SLA beyond this.

# Actor input Schema

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

Which surface to extract.

## `query` (type: `string`):

Keyword(s) for mode=search. V1 limitation: a filled keyword query hits /search\_result, whose note cards load via RedNote's signed API (deferred — D-007). Anonymous HTTP returns only a thin server-rendered state with NO note cards (zero ok rows — R-003). To get note cards, LEAVE THIS EMPTY to use the discovery feed (/explore), which returns ~25–31 full cards anonymously.

## `creatorUrl` (type: `string`):

RedNote /user/profile/<id> URL for mode=creator. Single URL.

## `creatorUrls` (type: `array`):

Array of /user/profile/<id> URLs for mode=creator. Use this for batch extraction (multiple profiles per run).

## `postUrl` (type: `string`):

RedNote /explore/<noteId> URL for mode=post (Bounded Beta). Single URL.

## `postUrls` (type: `array`):

Array of /explore/<noteId> URLs for mode=post. Use this for batch extraction. Each entry MUST include its own xsec\_token query param (e.g. https://www.xiaohongshu.com/explore/<noteId>?xsec\_token=<token>\&xsec\_source=pc\_feed) — entries without one are rejected as malformed (canon 03:16). The single postUrl field is exempt (Mode 3 cookie-only).

## `maxItems` (type: `integer`):

Hard CEILING on returned items per run (1–10000) — not a target. There is no pagination in V1: the Actor fetches one page per URL, so search/discovery returns roughly one feed page (~25–31 cards) per run no matter how high this is set. Creator mode scales by the number of creatorUrls (first-tab note cards per profile); post mode returns 1 row per URL. Counts OUTPUT ROWS across the whole run, not per URL — with several creatorUrls a low value may exhaust the budget before later URLs are fetched (skipped URLs are reported in the run diagnostics).

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

Sort order for search results. Not yet wired into the outgoing request (tracked as D-3) — accepted for forward compatibility only.

## `region` (type: `string`):

Bounded targeting hint, not a guarantee. Not yet wired into the outgoing request (tracked as D-3) — accepted for forward compatibility only.

## `language` (type: `string`):

Bounded language hint for results. Not yet wired into the outgoing request (tracked as D-3) — accepted for forward compatibility only.

## `sessionCookie` (type: `string`):

Optional. Bring-your-own session for post detail. Stored encrypted, never logged, never written to output. Using it may risk your RedNote account.

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

Apify Proxy or custom proxy settings.

## `includeDiagnostics` (type: `boolean`):

Include verbose diagnostic details in the run summary.

## `dedupe` (type: `boolean`):

Skip already-seen notes in subsequent runs — keyed by noteId for discovery cards and by a synthetic key (creator userId + title + cover) for creator-tab cards, which don't carry a noteId. Uses a persistent seen-set in KeyValueStore. Recommended for scheduled/recurring runs.

## `enableIntelligenceAi` (type: `boolean`):

OFF by default. Opt-in flag enabling Tier 3 LLM-derived intelligence enrichment (brand-sentiment / theme clustering / creator-fit / cross-source join-keys) on the `intelligence` namespace. When enabled, successful enrichment bills the `intelligence_ai` PPE event. Requires `llmApiKey` when enabled. This flag is intentionally OFF by default and MUST remain OFF by default across future schema versions — a future schema change must NOT silently default it ON (spec risk: opt-in flag survival).

## `llmApiKey` (type: `string`):

Optional. Bring-your-own LLM API key for Tier 3 enrichment. Required when `enableIntelligenceAi` is true; ignored otherwise. Stored encrypted, never logged, never written to output (canon 08). The Actor NEVER bundles an operator LLM key — BYOK only. Implementer of the validateInput wiring (workflow G): enforce that `enableIntelligenceAi: true` without `llmApiKey` is an INVALID\_INPUT rejection (D-015 pattern); do NOT silently fall back to deterministic-only when the flag is on but the key is missing. The endpoint must be Ollama-compatible — set llmBaseUrl too.

## `llmBaseUrl` (type: `string`):

Required when `enableIntelligenceAi` is true. Base URL of an OLLAMA-COMPATIBLE chat endpoint (the Actor POSTs to `<baseUrl>/api/chat`). Example: https://your-ollama-host.example.com. OpenAI/Anthropic keys will NOT work — the endpoint must speak the Ollama chat API. Must be https:// . The Actor never bundles an operator LLM endpoint or key (BYOK only).

## `brandBrief` (type: `string`):

Optional. A brand brief used to score `creatorFit` (Tier 3 opt-in LLM). When `enableIntelligenceAi` is on AND this brief is supplied, the model scores each returned creator's fit against it (audience-theme overlap, tone, engagement quality) — the honest version of claim #7 / 'turn discover-creators into a decision product'. When `enableIntelligenceAi` is on but this brief is ABSENT, `creatorFit` is emitted as `null` (the other three Tier-3 subfields — brandSentiment, themeClusters, joinKeys — still compute and `intelligence_ai` still bills). The brief is NOT a credential (not secret), but it is customer input and is NEVER logged or persisted to `RUN_SUMMARY` (canon 08 minimalism). It IS prompt-injection-guarded before any model call (canon 09 §Security — an unsafe brief degrades the whole Tier-3 enrichment to null, no model call). Whitespace-only is treated as absent. Ignored when `enableIntelligenceAi` is false. Amends D-027 (D-028).

## `outputTransform` (type: `object`):

Optional (D-031 / claim #4). Reshape the `data` payload of each delivered row and/or classify the run. Provide as a JSON object with any of three independent knobs, all optional: `fields` (array of dot-paths to KEEP — the row envelope is always kept; unresolvable paths are dropped, never fabricated as null), `flatten` (boolean — flatten nested objects to dot-keyed single-level entries; arrays stay at their leaf key), `classify` (array of dot-paths to group the run's rows by into per-class counts emitted to RUN\_SUMMARY; does NOT alter dataset rows). Example: {"fields":\["basicInfo.redId"],"flatten":true,"classify":\["noteCard.type"]}. `fields`/`flatten` are applied per-row AFTER billing fires on the original entity (charge-safe) and the row is re-validated against output.v1 — a transform that broke the contract is reverted, so a schema-invalid row never reaches the dataset. The transformed row is emitted on the additive `surface: "transformed"` oneOf branch (permissive `data: object|null`); schemaVersion is unchanged (2.0.0). `classify` keys are PII-guarded (each tuple component truncated to 80 chars; >100 distinct classes collapse into an `_other` bucket — E-001 / canon 08). Never throws — a bad dot-path or malformed shape degrades to 'no transform'. Absent ⇒ no transform, rows ship on their native surface.

## Actor input object example

```json
{
  "mode": "search",
  "creatorUrls": [],
  "postUrls": [],
  "maxItems": 100,
  "includeDiagnostics": false,
  "dedupe": false,
  "enableIntelligenceAi": false
}
```

# Actor output Schema

## `rows` (type: `string`):

One dataset row per fetch, across all modes. Envelope fields: surface (search | creator | post | insight | transformed), requestStatus (ok | partial | auth\_required | not\_found | blocked | error), url, data (surface-specific payload, null on non-ok), confidence (stable | best\_effort | experimental), fetchedAt (ISO 8601), schemaVersion (2.0.0), and an additive intelligence namespace (Tier 1 deterministic + Tier 3 opt-in LLM, absent/null on non-enriched rows). The data payload shape is surface-specific and enforced at runtime by AJV against output.v1 — see the README 'Output' section for each surface's fields. Branch on requestStatus before consuming data; treat 'partial' as 'reached the surface but no cards' (e.g. keyword search returns no note cards in V1 — R-003), not as a failure. Field-level detail + the dataset-view column layout are in ../schemas/dataset.apify-schema.json.

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

A single RUN\_SUMMARY record in the default key-value store: diagnostics counters, outcome (SUCCEEDED | FAILED | ABORTED), abortReason (null unless aborted), billingEvents (the names of the PPE events that may fire, not counts), and an optional classify object (per-class row counts from the optional outputTransform.classify input — absent unless requested). Carries no secret inputs (sessionCookie / llmApiKey are never persisted here). Use it to diagnose a run that delivered no ok rows or that aborted.

# 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 = {
    "creatorUrls": [],
    "postUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("protocol/rednote-api").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 = {
    "creatorUrls": [],
    "postUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("protocol/rednote-api").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 '{
  "creatorUrls": [],
  "postUrls": []
}' |
apify call protocol/rednote-api --silent --output-dataset

```

## MCP server setup

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

```

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/f2hqNgXOAwW7gOFjF/builds/T2WlqULajaxAwq80D/openapi.json
