# Internal test build — use wechat-official-account-scraper (`korado_labs/wechat-oa-dev-test`) Actor

Internal QA twin. Use the supported product instead: https://apify.com/korado\_labs/wechat-official-account-scraper

- **URL**: https://apify.com/korado\_labs/wechat-oa-dev-test.md
- **Developed by:** [Korado Labs](https://apify.com/korado_labs) (community)
- **Categories:** News, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.00 / 1,000 article search results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## WeChat Official Account Scraper — Articles, Accounts & Change Tracking

**Scrape WeChat Official Account (公众号, gongzhonghao) articles and account profiles from publicly accessible sources, and keep the results up to date automatically.** This WeChat scraper searches articles by keyword, finds accounts by name, extracts the full text of public article pages, and — unlike a one-shot scraper — remembers what it has already seen so later runs return **only what is new or edited**. Results are delivered as structured JSON, CSV or Excel, or pushed to your own endpoint through signed webhooks.

No login, no cookies, and no account credentials are required or accepted.

***

### What this WeChat scraper does

| Capability | Details |
| --- | --- |
| Article search | Find public WeChat articles by keyword, with pagination and a `since` date filter |
| Account search | Resolve public Official Account profiles by name |
| Account article history | Pull the recent public article list for a named account |
| Article detail extraction | Fetch a public `mp.weixin.qq.com` article and extract title, author, publish time and full text |
| **Change tracking** | Detect **new**, **edited** and **removed** articles across runs — the differentiating feature |
| **Signed webhooks** | HMAC-SHA256 signed delivery to your endpoint, with SSRF protection and retries |
| Structured export | JSON, CSV, Excel, or direct API/integration access |

***

### Why choose this WeChat Official Account scraper

Most WeChat scrapers return a snapshot: you run them, you get rows, and every later run returns the same rows again — which you then pay for again and have to de-duplicate yourself. This Actor is built around **retained state**.

- **Incremental sync, not repeated snapshots.** Every article version is fingerprinted with a content hash. Re-running a monitor returns only genuinely new or genuinely edited articles.
- **You are not billed twice for the same row.** Unchanged articles are skipped before charging, so a daily monitor costs a fraction of a daily full re-scrape.
- **Real change detection.** Rows carry a `changeType` of `new` or `modified`, plus a `status` of `active`, `modified` or `unavailable`, so you can see silently edited and deleted posts — a common compliance and PR signal.
- **A durable outbox.** A row is only marked delivered after it is actually written and charged. If a run crashes mid-way, the next run resumes instead of losing or duplicating data.
- **Zero setup for state.** Monitoring works out of the box using your own Apify key-value store. PostgreSQL is optional, and only needed if you want to run multiple monitors concurrently.
- **Errors are never billed.** Blocked pages, empty results and invalid input produce unbilled diagnostic rows, not charges.
- **Hardened webhook delivery.** Webhook targets are DNS-resolved and pinned, private and reserved IP ranges are refused, redirects are not followed, and every payload is HMAC signed.

***

### Supported operations

| `operation` | What it returns | Billed event |
| --- | --- | --- |
| `articleSearch` | Public articles matching a keyword | `search-result` |
| `accountSearch` | Public Official Account profiles | `account-result` |
| `accountRecentArticles` | Recent public articles for a named account | `search-result` |
| `articleDetail` | Full text and metadata for public article URLs | `article-detail` |
| `monitorAccounts` | Only new/changed articles for a watchlist | `search-result` + `monitored-account-day` |

Add `"resolveLinks": true` to any article operation to replace the search index's
session-bound redirect links with the real `mp.weixin.qq.com` article URL
(one extra request per row, so runs take longer). Row identity (`articleId`)
comes from the source's stable document id, so change tracking works the same
with or without link resolution.

***

### Quick start

1. Click **Try for free** and open the Input tab.
2. Choose an `operation` — start with `articleSearch` and a `keyword` such as `科技`.
3. Click **Start**, then download results as JSON, CSV or Excel from the **Storage** tab.

To start monitoring, switch `operation` to `monitorAccounts`, add public account display names, wxids, or `gh_` ids to `watchlist`, and schedule the Actor to run daily. The first run captures a baseline; every run after that returns only changes.

***

### Input example

```json
{
  "operation": "articleSearch",
  "keyword": "人工智能",
  "maxPages": 3,
  "since": "2026-01-01",
  "resolveLinks": true
}
```

Monitoring with webhook delivery:

```json
{
  "operation": "monitorAccounts",
  "watchlist": ["人民日报", "rmrbwx", "gh_363b924965e9"],
  "maxPages": 2,
  "webhookUrl": "https://example.com/hooks/wechat"
}
```

***

### Output example

```json
{
  "schemaVersion": 1,
  "recordType": "article",
  "articleId": "9f2c1b7e5a…",
  "url": "https://mp.weixin.qq.com/s?__biz=…",
  "title": "人工智能行业观察",
  "accountName": "示例公众号",
  "publishedAt": "2026-03-04T02:11:00.000Z",
  "status": "modified",
  "changeType": "modified",
  "contentHash": "3b7f…",
  "contentText": "…"
}
```

Every row carries `schemaVersion` and `recordType` (`article`, `account`, `monitorSummary` or `error`), so downstream loads never break on a mixed dataset.

***

### How to use the WeChat Official Account API

Run the Actor programmatically with the Apify API. Replace `YOUR_TOKEN` with your API token.

**cURL**

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~wechat-official-account-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"operation":"articleSearch","keyword":"科技","maxPages":2}'
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("YOUR_USERNAME/wechat-official-account-scraper").call(
    run_input={"operation": "articleSearch", "keyword": "科技", "maxPages": 2}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["url"])
```

**JavaScript**

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_USERNAME/wechat-official-account-scraper').call({
  operation: 'articleSearch',
  keyword: '科技',
  maxPages: 2,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

***

### Coverage honesty: what the public index can and cannot see

Results come from the public web index of WeChat Official Account content.
Coverage is strongest for keyword search. For a specific account's history,
recall depends on how that account's articles rank in the public index:
mid-size accounts usually return rows, while the very largest media accounts
(whose names are quoted by thousands of other accounts) may return few or none.
Raise `maxPages` to search deeper. If an account returns nothing, that is an
index-coverage limit, not an error — the run is free in that case.

Account search (`accountSearch`) uses the public account vertical where
available and automatically falls back to deriving publisher profiles
(name + `gh_` id) from public article pages where that vertical is
region-gated. Rows from the fallback carry the account's `gh_` id but not its
vanity `wxid`. To reach the region-gated vertical directly, configure a China
(CN) residential exit under **Proxy configuration** (or pass your own
`proxyUrls`); without one, the fallback keeps account search working
everywhere.

Date filtering is applied client-side (`since`): the index no longer honors
server-side date parameters, so results are filtered after fetching. Article
identity is stable across runs — the source's per-document id is used, never
its session-bound redirect URL (verified across sessions, hours, and
datacenters).

***

### Change monitoring and webhooks

Schedule `monitorAccounts` daily and the Actor will:

1. Fetch each watchlist account's recent public articles.
2. Compare each article against the retained content hash.
3. Emit only `new` and `modified` rows, and charge only for those.
4. Optionally POST them to your `webhookUrl` in batches of up to 100 events.

Webhook requests include `x-signature-256` (HMAC-SHA256 of the body), `x-wechat-monitor-delivery` (an idempotency key) and `x-delivery-attempt`. Verify the signature before trusting a payload. Signed delivery requires a `WEBHOOK_SECRET` of at least 32 characters; without it, webhook input is rejected rather than sent unsigned.

***

### Pricing

This Actor uses **pay per event**: you pay for result rows, not for compute time or run duration.

| Event | Charged when | Price |
| --- | --- | --- |
| `search-result` | One article row is returned | **$0.007** |
| `account-result` | One account profile row is returned | **$0.010** |
| `article-detail` | One full article page is extracted | **$0.020** |
| `monitored-account-day` | One account is checked on one day | **$0.050** |

**Worked examples**

- 1,000 article search results → **$7.00**
- 5,000 article rows per month → **$35.00**
- 100 full article extractions → **$2.00**
- 25 accounts monitored daily for 30 days, ~1,125 changed articles → 25 × 30 × $0.05 + 1,125 × $0.007 ≈ **$45.38/month**

Starting an Actor run is free, and the following are **never billed**: failed or blocked requests, empty result sets, invalid input, error rows, and unchanged articles during monitoring.

***

### Free plan and limits

The Actor runs on the Apify **Free plan** using your monthly platform credits. Practical limits to be aware of:

- `maxPages` accepts 1–50 per run; `watchlist` and `articleUrls` accept up to 200 items.
- Requests are rate limited (2.5 s between requests by default) to stay polite to public sources.
- A run stops cleanly when it reaches your configured pay-per-event limit and reports `budgetLimitReached` instead of failing.
- Concurrent `monitorAccounts` runs sharing one state store are refused with a clear message; configure PostgreSQL for concurrency.

***

### Integrations

Output works directly with **Make**, **Zapier**, **n8n**, **LangChain**, **Google Sheets**, **Slack**, and any HTTP endpoint through webhooks. Datasets can be pulled through the Apify API in JSON, CSV, Excel, XML or RSS, and the Actor is usable as an MCP tool by AI agents.

***

### Use cases

- **Brand and PR monitoring** — catch new mentions, and catch silently edited or deleted posts.
- **Competitive intelligence** — track competitor Official Accounts and build a publishing timeline.
- **Market and academic research** — build a retained corpus of Chinese-language articles with full text.
- **Compliance and risk** — evidence of what a public account published, and when it changed.
- **AI and RAG pipelines** — feed a continuously updated, de-duplicated Chinese-language corpus into a vector store.

***

### What this Actor does not do

Honest scope, so you can evaluate before paying:

- It does **not** return read counts, like counts, 在看, share or collect metrics.
- It does **not** return article comments or comment replies.
- It does **not** return WeChat Index time series, registration or principal/company records.
- It does **not** log in, use accounts or credentials, or call private WeChat APIs. (The public search index sets anonymous session cookies, which the Actor replays — no login is ever involved.)

Those datasets are not available from publicly accessible endpoints, and this Actor is deliberately built to stay within public sources. Coverage depends on what public indexes expose, which varies by account and keyword.

***

### FAQ

**What is a WeChat Official Account (公众号)?**
An Official Account, or gongzhonghao, is a publisher account on WeChat used by media, brands and government bodies to publish articles to subscribers. Public articles are served from `mp.weixin.qq.com`.

**Do I need a WeChat account or cookies?**
No. The Actor only reads publicly accessible pages and never accepts credentials.

**How does change detection work?**
Each article's normalized content is hashed. If the hash changes between runs, the row is emitted with `changeType: "modified"`. If the article disappears, its status becomes `unavailable`.

**Do I need a database?**
No. Monitoring state is stored in your own Apify key-value store by default. A `DATABASE_URL` for PostgreSQL is optional and only needed for concurrent monitor runs.

**Will I be charged for empty or failed runs?**
No. Only real result rows are billed.

**Can I export to CSV or Excel?**
Yes — from the Storage tab, or through the Apify API in JSON, CSV, Excel, XML and RSS.

**How often should I schedule monitoring?**
Daily is typical. Hourly is supported, but increases `monitored-account-day` charges only once per account per day, so cost stays predictable.

**Can I pass a wxid or gh\_ id?**
Yes. `account` and `watchlist` accept a public display name, a wxid such as `rmrbwx`, or a `gh_` id. Identifiers are resolved through public account search.

***

### Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| Zero rows for a keyword | The public index returned nothing. Try a broader keyword, or raise `maxPages`. You are not billed. |
| `blocked` count above zero | The public source rate limited or challenged the request. Increase `MIN_REQUEST_INTERVAL_MS` and retry later. |
| `webhookUrl requires WEBHOOK_SECRET` | Set a `WEBHOOK_SECRET` of 32+ characters; unsigned delivery is intentionally not allowed. |
| Webhook rejected as unsafe | The URL resolved to a private, loopback or reserved address. Use a public HTTPS endpoint. |
| `another monitor run already holds the state lease` | A previous monitor run is still active. Wait for it, or configure PostgreSQL for concurrent runs. |
| Account not found in `monitorAccounts` | Use the exact public display name, a wxid, or a `gh_` id. Run `accountSearch` first to confirm it. |

***

### Legal and compliance

This Actor accesses only publicly accessible web pages. It does not log in, does not use or accept WeChat credentials, does not bypass access controls, and does not reverse-engineer private protocols. It applies conservative rate limiting by default.

You are responsible for how you use the extracted data, including compliance with applicable data protection laws such as GDPR and PIPL, and with the terms of the sources you target. Personal data should only be collected where you have a lawful basis. WeChat and 微信 are trademarks of Tencent Holdings Limited. This Actor is not affiliated with, endorsed by, or sponsored by Tencent.

***

### Support

Open an issue on the Actor's **Issues** tab with your run ID and input, and you will get a response. Breaking changes are announced in the changelog before they ship.

# Actor input Schema

## `operation` (type: `string`):

Which public-source operation to run: search indexed articles, search accounts, list an account's recent indexed articles, extract detail for specific article URLs, or monitor accounts for new and changed articles.

## `keyword` (type: `string`):

Search term for articleSearch and accountSearch. If articleSearch is run with no other source fields, the Actor uses the demo keyword 科技 so the daily quality check returns real rows. Chinese terms generally have the best index coverage.

## `account` (type: `string`):

Required for accountRecentArticles. Accepts the public display name, a wxid such as rmrbwx, or a gh\_ id. Results are limited to publicly indexed articles.

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

HTTPS mp.weixin.qq.com article URLs for articleDetail. Do not set a schema default here — a default array would be injected into unrelated operations.

## `watchlist` (type: `array`):

Required for monitorAccounts. Each value may be a public display name, a wxid, or a gh\_ id. Only new or changed articles are returned and billed on later runs. No schema default — an empty default would contaminate other operations.

## `since` (type: `string`):

Optional ISO-8601 date/datetime. Rows with known publication times older than this are skipped.

## `maxPages` (type: `integer`):

Stops earlier when pages are empty or fully duplicate. More pages do not guarantee more coverage.

## `webhookUrl` (type: `string`):

Optional monitor delivery URL. Must be public HTTPS; private, loopback, link-local and credential-bearing URLs are rejected. Payloads are HMAC-SHA256 signed.

## `resolveLinks` (type: `boolean`):

Replace session-bound Sogou redirect links in results with the real mp.weixin.qq.com article URL (one extra request per row, so runs are slower). Row identity and change tracking are stable either way.

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

Optional Apify Proxy settings. Direct account search in the source index is geo-limited: a China (CN) residential exit restores it. Without a proxy the Actor automatically derives accounts from the article index instead. Proxies also add resilience against source rate limits.

## `proxyUrls` (type: `array`):

Bring-your-own HTTP(S) proxies, e.g. http://user:pass@host:port, rotated per request. Used only when Proxy configuration above is empty or unavailable.

## Actor input object example

```json
{
  "operation": "articleSearch",
  "keyword": "科技",
  "maxPages": 3,
  "resolveLinks": false
}
```

# Actor output Schema

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

Articles, accounts, monitor summaries, and typed unbilled errors from the default dataset.

## `runStats` (type: `string`):

Fetched, normalized, billed, blocked, partial-source, budget-limit, webhook-failure counters, and which state backend was used.

# 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 = {
    "keyword": "科技"
};

// Run the Actor and wait for it to finish
const run = await client.actor("korado_labs/wechat-oa-dev-test").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 = { "keyword": "科技" }

# Run the Actor and wait for it to finish
run = client.actor("korado_labs/wechat-oa-dev-test").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 '{
  "keyword": "科技"
}' |
apify call korado_labs/wechat-oa-dev-test --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,korado_labs/wechat-oa-dev-test"
        }
    }
}

```

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/gdhTx1hgSXskK4YnP/builds/lcWY4sD4RW3mJFRPT/openapi.json
