# WeChat Official Account API in Python for RAG (2026)

> A Python client that finds a WeChat Official Account, lists its recent articles and saves each one as a Markdown file with front matter, ready for chunking and embedding.

Canonical page: https://okdata.online/guides/wechat-official-account-api-python/

WeChat's own API only serves accounts you administer, so for any public account this guide calls three okdata endpoints from Python: Official Account search, Official Account article history and Official Account article detail. The worked example is 14 calls and costs $0.84, with the search counted at a full page; a failed call is not charged.

Updated 2026-11-12

What does the official WeChat API cover? An Official Account's server-side API starts with an access token, which you get from the account's AppID and AppSecret and which lasts 7,200 seconds. With it, the publishing API's `freepublish_batchget` returns the articles your account has published, and other calls return drafts, material and statistics. A third-party platform can make the same calls for an account that has authorised it. So if the account is yours, use the official API: it is free, complete and gives you the original HTML. What it does not do is read accounts you do not administer, which is what a research or competitor knowledge base needs.

This guide covers that case. You build a small Python client that finds an account by name, lists its recent articles, fetches each one's full text and writes it as a Markdown file with YAML front matter: title, account, date and source link. The files drop straight into a chunker and an embedding job, and every chunk can cite its source.

## Step by step

### Step 1: set up

Python 3.9 or newer, `requests`, and your key in an environment variable. No SDK, no WeChat login.

```bash
pip install requests
export OKDATA_API_KEY=okd_...
```

Each call is a GET to `https://api.okdata.online` plus the endpoint path with the key as a bearer token. Success is `"code": "ok"`; an error carries `code`, `message`, `request_id` and a `retry` hint (`yes` means try again after `retry_after_seconds`).

### Step 2: find the account

[Official Account search](https://okdata.online/apis/wechat_mp/accounts/search/) takes the account name as `query`. Each result has `id` (the original ID, `gh_…`), `username` (the WeChat ID), `name` and `followers`. Keep only an exact `name` match; names are not unique and the first hit is not always the account you mean.

Search is priced per account returned: $0.058 each, at most $0.58 a call. An exact name usually returns one or a few. Store the gh_ ID after the first run and you never need to search for that account again.

```bash
curl -G "https://api.okdata.online/v1/wechat_mp/accounts/search" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'query=少数派'
```

### Step 3: list recent articles

[Official Account article history](https://okdata.online/apis/wechat_mp/accounts/articles/list/) takes the gh_ ID or WeChat ID as `id` and returns 10 articles per call, newest first, for $0.02. Each item has `id`, `title`, `url`, `published_at` and a summary in `text`. Page with `cursor` until `page.has_more` is false or your page cap is reached.

### Step 4: fetch each article as text

[Official Account article detail](https://okdata.online/apis/wechat_mp/articles/get/) takes the article `url` and returns the body as plain text in `text`, with paragraphs and headings on their own lines and inline images removed, for $0.02. It also returns `title`, `published_at` and `author` (the account's gh_ ID and name). With `view=full`, `ext.author_byline` names the writer and `ext.source_url` points to the original when the piece is a repost.

```bash
curl -G "https://api.okdata.online/v1/wechat_mp/articles/get" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'url=https://mp.weixin.qq.com/s/Vt8nR2kLq5XwJ0cYhPa3dE' \
  --data-urlencode 'view=full'
```

### Step 5: the client

The script below runs the worked example. It searches once, reads up to `MAX_PAGES` of history, writes an index of everything it listed, then fetches up to `MAX_ARTICLES` articles it does not have on disk yet and saves each as `kb/wechat/<id>.md`. Run it again next week and it only pays for new articles.

```python
import os
import pathlib
import re
import time
import uuid
import requests

API = "https://api.okdata.online"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['OKDATA_API_KEY']}"
OUT = pathlib.Path("kb/wechat")
MAX_PAGES = 3      # history pages, 10 articles each
MAX_ARTICLES = 10  # full-text calls per run


class ApiError(Exception):
    def __init__(self, body):
        super().__init__(f"{body['code']}: {body['message']} (request {body['request_id']})")
        self.code = body["code"]


def call(path, **params):
    key = str(uuid.uuid4())  # same key on every retry: a replay is not charged again
    for attempt in range(3):
        body = SESSION.get(API + path, params=params, headers={"Idempotency-Key": key}, timeout=90).json()
        if body["code"] == "ok":
            return body
        if body.get("retry") == "yes" and attempt < 2:
            time.sleep(body.get("retry_after_seconds") or 5)
            continue
        raise ApiError(body)


def find_account(name):
    accounts = call("/v1/wechat_mp/accounts/search", query=name)["data"]["items"]
    match = next((a for a in accounts if a["name"] == name), None)
    if match is None:
        raise SystemExit(f"no account named {name!r}; got {[a['name'] for a in accounts]}")
    return match


def history(account_id):
    cursor = None
    for _ in range(MAX_PAGES):
        body = call("/v1/wechat_mp/accounts/articles/list", id=account_id, **({"cursor": cursor} if cursor else {}))
        yield from body["data"]["items"]
        if not body["page"]["has_more"]:
            return
        cursor = body["page"]["next_cursor"]


def quoted(s):
    return '"' + (s or "").replace("\\", "\\\\").replace('"', '\\"') + '"'


def to_markdown(article, account):
    ext = article.get("ext", {})
    front = [
        "---",
        f"title: {quoted(article['title'])}",
        f"account: {quoted(account['name'])}",
        f"account_id: {account['id']}",
        f"byline: {quoted(ext.get('author_byline'))}",
        f"published: {article['published_at']}",
        f"source: {article['url']}",
        "---",
    ]
    return "\n".join(front) + f"\n\n# {article['title'] or ''}\n\n{article['text']}\n"


def main(name):
    OUT.mkdir(parents=True, exist_ok=True)
    account = find_account(name)
    listed = list(history(account["id"]))  # newest first
    with open(OUT / "index.tsv", "w", encoding="utf-8") as f:
        f.writelines(f"{a['published_at']}\t{a['title']}\t{a['url']}\n" for a in listed)

    path_of = lambda a: OUT / (re.sub(r"[^\w-]", "_", a["id"]) + ".md")
    todo = [a for a in listed if not path_of(a).exists()][:MAX_ARTICLES]
    for a in todo:
        try:
            full = call("/v1/wechat_mp/articles/get", url=a["url"], view="full")["data"]["item"]
        except ApiError as err:
            if err.code == "not_found":  # deleted since it was listed; not charged
                continue
            raise
        path_of(a).write_text(to_markdown(full, account), encoding="utf-8")
        print("saved", full["title"])


if __name__ == "__main__":
    main("少数派")
```

The first run is the worked example: 1 search, 3 history pages, 10 articles. Backfilling the same account's last 300 articles would add $6.60.

### Step 6: chunk and index

Each file is front matter plus the article. Split the body on blank lines into chunks of a few hundred Chinese characters, and copy `title`, `account`, `published` and `source` into each chunk's metadata so an answer can cite the article and link to it. Most loaders read YAML front matter directly; if yours does not, parse the block between the `---` lines yourself.

## Good to know

- **Text, not layout.** Detail returns plain text. Tables collapse into lines and inline images are gone; the cover URL is in `images`. If you need the article as it looked, archive the HTML another way.
- **Reposts.** Many accounts republish other people's pieces. `ext.source_url` and `ext.copyright` on the detail help you drop duplicates before they skew retrieval.
- **Deleted is free.** Articles removed after listing return `not_found` and are not charged; the client skips them.
- **Retries are safe.** The client reuses one `Idempotency-Key` across retries, and the same request with the same key within 24 hours returns the stored result with `meta.replayed: true` and no new charge.
- **Untrusted text.** Article text is written by strangers. Tell your model to treat retrieved passages as data, not instructions.
- **Rights.** Index for internal research, keep the source link with every chunk and do not serve whole articles back to users.

For the shorter version of the Markdown step, see [turning WeChat articles into clean text for RAG](https://okdata.online/answers/wechat-articles-to-text-for-rag/). If you only have names and need IDs first, see [finding an account's gh_ ID](https://okdata.online/answers/find-wechat-official-account-id/).

## What the example costs

One search for 少数派 (sspai), three history pages (about 30 articles), then the 10 newest articles saved as Markdown files

| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| [Official Account search](https://okdata.online/apis/wechat_mp/accounts/search/) | 1 | $0.058 | $0.58 |
| [Official Account article history](https://okdata.online/apis/wechat_mp/accounts/articles/list/) | 3 | $0.02 | $0.06 |
| [Official Account article detail](https://okdata.online/apis/wechat_mp/articles/get/) | 10 | $0.02 | $0.20 |
| Total | 14 |  | $0.84 |

Prices in force when this page was built. A failed call costs nothing.

## In your AI (MCP)

Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.

Say: Find the WeChat Official Account 少数派, take its 10 newest articles and give me each one as Markdown with the title, account, publish date and source link at the top.

```json
{
  "endpoint": "wechat_mp.accounts.search",
  "params": {
    "query": "36氪"
  },
  "idempotency_key": "<a new UUID>"
}
```

MCP: `https://api.okdata.online/mcp`

## Endpoints in this guide

- [Official Account search](https://okdata.online/apis/wechat_mp/accounts/search/) `wechat_mp.accounts.search`: $0.058 per account · at most $0.58 per call
- [Official Account article history](https://okdata.online/apis/wechat_mp/accounts/articles/list/) `wechat_mp.accounts.articles.list`: $0.02 per call · about 10 articles per page
- [Official Account article detail](https://okdata.online/apis/wechat_mp/articles/get/) `wechat_mp.articles.get`: $0.02 per call

## More about WeChat Official Accounts

- [WeChat Official Accounts API](https://okdata.online/apis/wechat_mp/)
- [How to get a WeChat Official Account's articles and full text](https://okdata.online/guides/wechat-official-account-articles/)
- [Why can't ChatGPT or Claude read a WeChat article link, and how do I fix it?](https://okdata.online/answers/ai-read-wechat-article-link/)
- [How do I export all articles of a WeChat Official Account without logging in?](https://okdata.online/answers/export-wechat-official-account-articles/)
- [How do I monitor competitors' WeChat Official Accounts for new articles?](https://okdata.online/answers/monitor-competitor-wechat-official-accounts/)
- [What is __biz in a WeChat article link?](https://okdata.online/answers/wechat-biz-parameter/)
- [What is a WeChat Official Account's original ID (gh_…)?](https://okdata.online/answers/wechat-gh-original-id/)
- [How much does a WeChat Official Account article API cost?](https://okdata.online/answers/wechat-official-account-api-price/)
- [Official platform APIs vs third-party data APIs (2026)](https://okdata.online/compare/official-apis-vs-data-apis/)
- [MCP servers for social media data: self-hosted vs hosted](https://okdata.online/compare/social-media-data-mcp-servers/)
- [WeChat article APIs: official vs third-party vs open-source (2026)](https://okdata.online/compare/wechat-article-data-apis/)
- [Ways to get WeChat Official Account data: the official API, RSS bridges and data APIs](https://okdata.online/compare/wechat-official-account-data-apis/)
- [Xiaohongshu, Douyin and WeChat MCP servers: self-hosted vs hosted](https://okdata.online/compare/xiaohongshu-douyin-wechat-mcp-servers/)
- [Account posts API](https://okdata.online/roles/accounts-posts-list/)

## Sources

- [微信开放文档: 获取接口调用凭据 (getAccessToken)](https://developers.weixin.qq.com/doc/subscription/api/base/api_getaccesstoken.html) (read 2026-11-12)
- [微信开放文档: 获取已发布的消息列表 (freepublish_batchget)](https://developers.weixin.qq.com/doc/subscription/api/public/api_freepublish_batchget.html) (read 2026-11-12)
