# WeChat Official Account Scraper (`lanternlab318/wechat-official-account`) Actor

WeChat Official Account (公众号) articles with read, like and share counts in one row, comments with reply threads, article and account search, and account post history. Every row says whether collection was complete.

- **URL**: https://apify.com/lanternlab318/wechat-official-account.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Categories:** Social media, News
- **Stats:** 5 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $49.90 / 1,000 articles

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

## WeChat Official Account Scraper

A Lantern Lab Actor. Collect public data from WeChat Official Accounts (微信公众号): articles with their engagement counts, comments with reply threads, article and account search, and an account's post history. No WeChat login, no cookies, no account of your own.

Every row says how complete it is. When something could not be read, the row is marked `partial` or `failed` with the reason - a failed article is never reported as "0 comments".

### What it does

Pick one `mode` per run:

| Mode | Input | One row per |
| --- | --- | --- |
| `articles` | article links | article: title, author, account, publish time, text, plus reads, likes (赞), Wow (在看), shares, favorites and comment count - **in one row, one charge** |
| `comments` | article links | comment and reply, plus one summary per article |
| `articleSearch` | keywords | matching article, plus one summary per keyword |
| `accountSearch` | keywords | matching Official Account (with verified owner and original-article count), plus one summary per keyword |
| `accountArticles` | account IDs (`gh_...`) or exact names | article in the account's post history, plus one summary per account |

### What it does not do

- Only what WeChat shows publicly. WeChat shows the comments the author has featured (精选). When WeChat counts more comments and replies than it shows, the article's summary row is `partial` and says how many are hidden.
- No WeChat Index, search suggestions or mini-program search.
- No commenter IDs or avatars: only the public nickname and the region WeChat shows next to a comment.
- No scheduling or monitoring built in; use Apify schedules.

### Input

| Field | Default | Used by | Meaning |
| --- | --- | --- | --- |
| `mode` | `articles` | all | What to collect (see above). |
| `articleUrls` | - | `articles`, `comments` | Links like `https://mp.weixin.qq.com/s/...` or `https://mp.weixin.qq.com/s?__biz=...&mid=...&idx=...&sn=...`. |
| `includeText` | `true` | `articles` | Add the article body as plain text. |
| `includeHtml` | `false` | `articles` | Add the article body as HTML. |
| `maxCommentsPerArticle` | 100 | `comments` | Top-level comments per article. |
| `includeReplies` | `true` | `comments` | Collect replies under each comment. |
| `maxRepliesPerComment` | 20 | `comments` | Replies per comment thread. |
| `keywords` | - | search modes | One search per keyword. |
| `maxResultsPerKeyword` | 20 | search modes | Results per keyword. |
| `maxPagesPerKeyword` | - | search modes | Hard ceiling on the search pages read per keyword. Each page is one request and one `page` event. `1` means exactly one request per keyword and every result that page returns. Empty means no ceiling. |
| `searchSort` | `relevance` | search modes | `relevance`, `latest` or `popular`. |
| `searchTimeRange` | `any` | `articleSearch` | `any`, `day`, `week` or `halfYear`. |
| `accounts` | - | `accountArticles` | `gh_...` IDs, or names that match an account name or WeChat ID exactly. |
| `maxArticlesPerAccount` | 20 | `accountArticles` | Articles per account, newest first. |
| `publishedAfter`, `publishedBefore` | - | `accountArticles` | Date range (UTC, inclusive). |
| `maxUpstreamRequests` | derived from your input | all | Hard ceiling on the requests this run may send to the data source, across every mode and input line. Counted in HTTP attempts, so an internal retry uses one up. `0` means no request is sent at all. Leave it empty and the Actor works the ceiling out from the mode and the limits you set, which is enough to finish the run you asked for; your own value always wins. See Limits. |

Example - two articles with engagement counts:

```json
{
  "mode": "articles",
  "articleUrls": [
    "https://mp.weixin.qq.com/s?__biz=MzA5NDM0NDcxNg==&mid=2655753720&idx=1&sn=4c04102cef0abd595053213adbf8b0b5",
    "https://mp.weixin.qq.com/s/gB6VF0eC2tJcBX6RM4AYqA"
  ]
}
```

Example - comments and replies:

```json
{ "mode": "comments", "articleUrls": ["https://mp.weixin.qq.com/s/gB6VF0eC2tJcBX6RM4AYqA"], "maxCommentsPerArticle": 200 }
```

### Output

One dataset; `recordType` tells the rows apart. Four table views are predefined (Articles, Comments and replies, Search results, Account post history). A `RUN_SUMMARY` record in the key-value store has run totals.

Fields on every row:

| Field | Meaning |
| --- | --- |
| `recordType` | `article`, `comment`, `reply`, `article_search_result`, `account_search_result`, `account_article`, or a `*_summary` row for each input line. |
| `collectedAt` | When we collected the row (UTC). |
| `publishedAt` | When WeChat says the article, comment or reply was published (UTC). |
| `collectionStatus` | `complete`, `limit_reached` (stopped at your limit), `partial` (something WeChat shows could not be read), `empty` (WeChat shows nothing, e.g. 0 comments) or `failed` (nothing could be read - not the same as empty). |
| `statusReason` | Why the row is not complete, or what WeChat does not show. |

Article rows (`articles` mode): `articleId` (`<__biz>_<mid>_<idx>`), `articleUrl`, `shortUrl`, `title`, `author`, `digest`, `accountId` (`gh_...`), `accountName`, `accountAlias`, `accountBiz`, `publishedAt`, `contentType`, `isOriginal`, `sourceUrl`, `coverUrl`, `ipRegion`, `albumTitle`, `text`, `html`, `readCount`, `likeCount` (赞), `wowCount` (在看), `shareCount`, `favoriteCount`, `commentCount` (comments and replies), `commentsEnabled`.

Comment and reply rows: `articleId`, `articleUrl`, `commentId`, `parentId` (for replies: the comment's `commentId`), `text`, `authorNickname`, `ipRegion`, `isAccountReply`, `isPinned`, `likeCount`, `replyCount`, `repliesCollected`, `publishedAt`. Reply IDs are `<commentId>-<reply number>`.

`comments_summary`: `reportedCommentCount` (comments and replies WeChat counts - the same number as `commentCount` in `articles` mode), `commentsCollected`, `threadsWithReplies`, `expectedReplies`, `repliesCollected`, `threadsIncomplete`, `threadsLimited`.

Search rows: `keyword`, `rank`, and the article (`articleId`, `articleUrl`, `title`, `digest`, `accountName`, `publishedAt`) or account (`accountId`, `accountName`, `accountAlias`, `accountType`, `description`, `verified`, `verifiedEntity`, `originalArticleCount`, `avatarUrl`). Search summaries: `reportedTotal`, `resultsCollected`, `pagesRead`.

Account history rows: `accountId`, `accountName` (when the input was a name), `articleId`, `articleUrl`, `title`, `digest`, `positionInPost`, `contentType`, `isPaid`, `coverUrl`, `publishedAt`, `updatedAt`. `account_summary`: `articlesCollected`, `oldestPublishedAt`, `newestPublishedAt`.

Large IDs are strings. Counts are what WeChat reported at `collectedAt` and change over time.

#### Backup data source (`RUN_SUMMARY`)

This Actor reads one data source and has no backup channel, so `RUN_SUMMARY` always reports
`backupConfigured: false` and `backupUnavailableKind: "not_configured"`. Both are a statement about
how the Actor is built, never about this run: read `backupConfigured` first and stop there. What the
Actor's own source did is in `upstreamAttempts`, `upstreamBilled`, `upstreamCapped` and
`stoppedReason`, and what each input line returned is in its summary row. The fields are named the
same as in our other Actors so one rule can read every run summary. The human-readable
`backupUnavailable` sentence belongs to Actors that do have a backup channel and is not written here.

### Pricing

Pay per event:

| Event | Charged for |
| --- | --- |
| `article` | One article with its text and engagement counts (`articles` mode). Not charged when the link fails. |
| `page` | Each page WeChat returns in the other modes: up to 20 comments, a page of replies, a page of search results, a page of post history, or an account name lookup. A link WeChat rejects in `comments` mode still costs one page. |
| `comment` | Each comment or reply row. |
| `article-search-result` | Each article search result. |
| `account-search-result` | Each Official Account search result. |
| `account-article` | Each article from an account's post history. |

Summary rows are free. Replies that WeChat already shows under a comment cost no extra page. Set a maximum charge for the run and the Actor stops before going over it; the run summary says it stopped early.

### Limits

- `maxUpstreamRequests` is a hard ceiling, not a target: the run sends at most that many requests to the data source, counted in HTTP attempts, so an internal retry of a request that timed out uses one up. Reaching it gives you **incomplete results**: collection stops where it is, every row already written is kept, and the affected summary row is `partial` (when it had results) or `failed` (when it had none), with `stopReason` `upstream_request_cap` and a `statusReason` that says which ceiling stopped it, not WeChat. Nothing is billed for a request the ceiling stopped, because it is never sent.
- **Every run has this ceiling, including one that does not ask for it.** When you leave `maxUpstreamRequests` empty, the Actor works it out from the mode and the input you gave it: `input lines x requests per line x 2 + 2`. The requests one line needs are `2` for `articles` (engagement, then the body), `ceil(maxCommentsPerArticle / 10)` for `comments`, `ceil(maxResultsPerKeyword / 10)` for `articleSearch` and `accountSearch` (lowered to `maxPagesPerKeyword` when you set it) and `1 + ceil(maxArticlesPerAccount / 5)` for `accountArticles`. The doubling covers a retried request, the two spare cover the overlap between pages, and only the list the mode actually reads counts - so the default is enough to finish the run you asked for, and a single run cannot send an unbounded number of requests. One keyword and the default 20 results give 6. A value you pass yourself always wins, whether it is larger or smaller. The run summary reports which of the two was used in `upstreamCapSource` (`input` or `derived`) and the number itself in `upstreamCap`.
- `maxUpstreamRequests: 0` sends no request at all. Every input line is then reported as `failed` with `upstream_request_cap`, which is not the same as an empty result.
- The ceiling applies to the whole run, so with several keywords or links the last ones can be the incomplete ones. The run summary reports `upstreamAttempts` (HTTP attempts, the number the ceiling counts), `upstreamBilled` (attempts the data source answered), `upstreamCap`, `upstreamCapSource` and `upstreamCapped`.
- `maxPagesPerKeyword` limits the search pages per keyword. The page being read is always finished first, so `maxPagesPerKeyword: 1` with `maxResultsPerKeyword: 20` is exactly one request per keyword and every result that page returns. When WeChat still has more, the keyword is `partial` with `stopReason` `max_pages_per_keyword` - told apart from stopping at `maxResultsPerKeyword`, which is `limit_reached` - and `pagesRead` shows how many pages were read. When WeChat had no more results, the page limit cut nothing and the keyword stays `complete`.
- `maxPagesPerKeyword` is optional and has no default of its own: left empty, paging stops at `maxResultsPerKeyword`, when WeChat runs out of results, or at the request ceiling above, whichever comes first.

### Tips

- An article with 0 comments costs one `page` and returns an `empty` summary.
- A date range on `accountArticles` pages through newer posts first; old ranges on very active accounts read more pages.
- Search paging stops when two pages in a row add no new result, even when WeChat says it has more; the keyword summary is then `partial` and says how many pages were read.
- Use `articles` mode first to see `commentCount`, then run `comments` only for the articles you need.
- Links copied from WeChat carry tracking parameters; the Actor strips them and outputs a permanent `articleUrl`.

### Changes

- **Build 0.1.7 (2026-10-09):** fixed article `likeCount` and `wowCount`, which were swapped in earlier builds. `likeCount` is now likes (赞) and `wowCount` is Wow (在看). In article rows collected before build 0.1.7, swap the two values. Comment and reply `likeCount` was not affected.

### Feedback

Open an issue on this Actor with a sample link and the output you expected.

# Actor input Schema

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

articles: full article with read, like, wow, share, favorite and comment counts (one row per link). comments: comments and reply threads of each article. articleSearch: articles matching keywords. accountSearch: Official Accounts matching keywords. accountArticles: the post history of accounts.

## `articleUrls` (type: `array`):

WeChat article links: https://mp.weixin.qq.com/s/... or the long https://mp.weixin.qq.com/s?\_\_biz=...\&mid=...\&idx=...\&sn=... form.

## `includeText` (type: `boolean`):

articles mode: add the article body as plain text.

## `includeHtml` (type: `boolean`):

articles mode: add the article body as HTML. Rows get large.

## `maxCommentsPerArticle` (type: `integer`):

comments mode: top-level comments to collect per article.

## `includeReplies` (type: `boolean`):

comments mode: also collect the replies under each comment.

## `maxRepliesPerComment` (type: `integer`):

comments mode: replies to collect per comment thread.

## `keywords` (type: `array`):

One search per keyword, in Chinese or any language WeChat search supports.

## `maxResultsPerKeyword` (type: `integer`):

Results to collect for each keyword.

## `maxPagesPerKeyword` (type: `integer`):

Search modes: hard ceiling on the search pages read per keyword. Each page is one upstream request and one charged page event. The whole page that is read is kept, so 1 means exactly one request per keyword and every result that page returns. When WeChat still has more, the keyword summary is partial with stopReason "max_pages_per_keyword", which is not the same as stopping at maxResultsPerKeyword. Leave empty for no page ceiling; paging then stops at maxResultsPerKeyword, when WeChat runs out of results, or at maxUpstreamRequests, whichever comes first. Setting it also lowers the request ceiling the Actor derives when maxUpstreamRequests is empty.

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

Order of search results.

## `searchTimeRange` (type: `string`):

articleSearch: only articles published in this period.

## `accounts` (type: `array`):

Official Account IDs (gh\_...) or exact account names. A name is looked up first and must match an account name or WeChat ID exactly.

## `maxArticlesPerAccount` (type: `integer`):

Articles to collect per account, newest first.

## `publishedAfter` (type: `string`):

accountArticles: skip articles published before this date (UTC).

## `publishedBefore` (type: `string`):

accountArticles: skip articles published after this date (UTC).

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the requests this run may send to the WeChat data source, across every mode and every input line. Counted in HTTP attempts, so an internal retry uses one up. The run never sends more than this, whatever the input asks for; when the ceiling is reached, collection stops there and the rows already written are kept, with the affected summary row partial or failed and stopReason "upstream_request_cap", so results can be incomplete. 0 means no request is sent at all. Leave empty and the Actor works the ceiling out from the mode and the limits you set - input lines x requests per line x 2 + 2, which is 6 for one keyword and 20 results - so a run always has an upper bound; the run summary reports the number used in upstreamCap and where it came from in upstreamCapSource.

## Actor input object example

```json
{
  "mode": "articles",
  "articleUrls": [
    "https://mp.weixin.qq.com/s?__biz=MzA5NDM0NDcxNg==&mid=2655753720&idx=1&sn=4c04102cef0abd595053213adbf8b0b5"
  ],
  "includeText": true,
  "includeHtml": false,
  "maxCommentsPerArticle": 100,
  "includeReplies": true,
  "maxRepliesPerComment": 20,
  "keywords": [
    "复合肥"
  ],
  "maxResultsPerKeyword": 20,
  "searchSort": "relevance",
  "searchTimeRange": "any",
  "accounts": [
    "gh_c4bb1384fd05"
  ],
  "maxArticlesPerAccount": 20
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

No description

# 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 = {
    "articleUrls": [
        "https://mp.weixin.qq.com/s?__biz=MzA5NDM0NDcxNg==&mid=2655753720&idx=1&sn=4c04102cef0abd595053213adbf8b0b5"
    ],
    "keywords": [
        "复合肥"
    ],
    "accounts": [
        "gh_c4bb1384fd05"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/wechat-official-account").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 = {
    "articleUrls": ["https://mp.weixin.qq.com/s?__biz=MzA5NDM0NDcxNg==&mid=2655753720&idx=1&sn=4c04102cef0abd595053213adbf8b0b5"],
    "keywords": ["复合肥"],
    "accounts": ["gh_c4bb1384fd05"],
}

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/wechat-official-account").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 '{
  "articleUrls": [
    "https://mp.weixin.qq.com/s?__biz=MzA5NDM0NDcxNg==&mid=2655753720&idx=1&sn=4c04102cef0abd595053213adbf8b0b5"
  ],
  "keywords": [
    "复合肥"
  ],
  "accounts": [
    "gh_c4bb1384fd05"
  ]
}' |
apify call lanternlab318/wechat-official-account --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lanternlab318/wechat-official-account"
        }
    }
}
```

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/oiBsPM53AiSAPyjLq/builds/H7SFauZiiHcpWOPHP/openapi.json
