# How to Download WeChat Article Comments and Replies via the Comment API

> Learn how to download WeChat article comments and replies using the official comment API. This guide explains the workflow for data extraction and storage.

- Repository: [公众号文章工具箱/wechat-article-exporter](https://github.com/wechat-article/wechat-article-exporter)
- Tags: how-to-guide
- Published: 2026-05-26

---

**The wechat-article-exporter downloads WeChat article comments and their threaded replies by routing authenticated requests through a Nitro server proxy, paginating through the `appmsg_comment` API responses, and persisting the data to IndexedDB using separate Dexie stores for comments and replies.**

The `wechat-article/wechat-article-exporter` repository implements a complete workflow to extract user interactions from WeChat articles via the official comment API. This guide breaks down the technical pipeline—from credential forwarding and pagination handling to local storage architectur—using the actual source files and method signatures found in the codebase.

## Proxying the WeChat Comment API Through Nitro

Direct browser requests to WeChat's comment endpoints fail due to CORS restrictions and strict authentication requirements. The exporter solves this by implementing a server-side proxy endpoint that forwards client requests with valid session credentials.

In [`server/api/web/misc/comment.get.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/server/api/web/misc/comment.get.ts), the Nitro endpoint receives query parameters—including `__biz`, `uin`, `key`, and `pass_ticket`—and forwards them to `https://mp.weixin.qq.com/mp/appmsg_comment` using the `proxyMpRequest` utility. This returns the raw JSON payload from WeChat's **appmsg_comment** API to the client without modification.

The proxy handles the sensitive cookie-based authentication that WeChat requires, allowing the client-side downloader to operate without exposing credentials to the browser context directly.

## Downloading Top-Level Comments

The `Downloader` class in [`utils/download/Downloader.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/utils/download/Downloader.ts) orchestrates the comment retrieval process. When `downloader.startDownload('comments')` is invoked, the system iterates over each target article URL and executes `fetchComments()` (lines 71-84).

This method constructs the *getcomment* URL, injects the user's credential headers, routes the request through the private proxy managed by `ProxyManager`, and parses the JSON response. The downloader continues calling the API until `continue_flag` returns `false`, indicating all pages have been retrieved.

Each `CommentResponse` is temporarily held in memory before being committed to storage via `updateCommentCache()`. The method also checks the `reply_new.reply_total_cnt` field to determine if a comment has replies that need separate fetching.

## Fetching Comment Replies

WeChat stores replies separately from parent comments. After saving top-level comments, the downloader filters for entries where `reply_new.reply_total_cnt` exceeds the length of the already-fetched `reply_list`. For each match, it invokes `fetchCommentReply()` (lines 19-28 in [`utils/download/Downloader.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/utils/download/Downloader.ts)).

This method calls the *getcommentreply* endpoint with the comment's `content_id` and the last known `max_reply_id` parameter. The API returns up to 100 new replies per call. The method iteratively fetches until all replies are retrieved, persisting each `ReplyResponse` via `updateCommentReplyCache()`.

## Persisting Data to IndexedDB

The exporter uses Dexie (an IndexedDB wrapper) to maintain two distinct object stores for comment data:

- **`comment`** – Stores top-level comment JSON blobs, keyed by article URL. Defined in [`store/v2/comment.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/store/v2/comment.ts).
- **`comment_reply`** – Stores reply JSON blobs, keyed by both article URL and comment `content_id`. Defined in [`store/v2/comment_reply.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/store/v2/comment_reply.ts).

These wrappers provide async methods like `getCommentCache()` and `updateCommentReplyCache()` that abstract the IndexedDB transactions. The separation allows the UI to query comments independently from their reply threads, improving render performance for articles with thousands of interactions.

## Integrating with the Vue Frontend

The `useDownloader` composable in [`composables/useDownloader.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/composables/useDownloader.ts) (lines 49-68) bridges the download engine with the user interface. It instantiates the `Downloader` class, registers listeners for `download:progress` and `download:finish` events, and exposes a `download()` method that accepts an `onComment` callback.

When comments are successfully saved, the composable triggers UI updates such as success toasts or progress bars. The downloader also respects a configurable `maxRetries` parameter defined in [`utils/download/constants.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/utils/download/constants.ts), automatically selecting alternative proxies via `ProxyManager` when requests fail.

## Practical Implementation Examples

### Triggering a Comment Download from a Vue Component

```typescript
import useDownloader from '~/composables/useDownloader'

const { download } = useDownloader({
  onComment: (url) => console.log('Comments saved for', url)
})

// selectedUrls is an array of article URLs chosen in the UI
download('comment', selectedUrls)

```

### Manually Calling the Server Proxy

```typescript
import { $fetch } from 'ofetch'

async function getComments(biz: string, commentId: string) {
  const resp = await $fetch('/api/web/misc/comment.get', {
    query: {
      __biz: biz,
      comment_id: commentId,
      key: 'xxx',
      uin: 'yyy',
      pass_ticket: 'zzz'
    }
  })
  return resp
}

```

### Inspecting Cached Comment Data

```typescript
import { getCommentCache } from '~/store/v2/comment'
import { getCommentReplyCache } from '~/store/v2/comment_reply'

async function dumpComments(url: string, contentId: string) {
  console.log('Top-level:', await getCommentCache(url))
  console.log('Replies:', await getCommentReplyCache(url, contentId))
}

```

## Summary

- **Server Proxy Required**: The Nitro endpoint at [`server/api/web/misc/comment.get.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/server/api/web/misc/comment.get.ts) forwards authenticated requests to WeChat's `appmsg_comment` API to bypass CORS and handle session cookies.
- **Two-Stage Download**: `Downloader.fetchComments()` retrieves paginated parent comments while `fetchCommentReply()` handles nested reply threads separately using `content_id` and `max_reply_id`.
- **Pagination Handling**: The downloader monitors `continue_flag` in API responses to iterate through all comment pages automatically until exhaustion.
- **Separate Storage**: Comments and replies are stored in distinct IndexedDB tables (`comment` and `comment_reply`) via Dexie wrappers for efficient querying and lazy-loading.
- **Resilient Architecture**: The `ProxyManager` and configurable retry logic in [`utils/download/constants.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/utils/download/constants.ts) ensure reliable downloading even when individual proxies fail.

## Frequently Asked Questions

### Why does the exporter need a server proxy to download comments?

WeChat's comment API endpoints require valid session cookies and specific credential parameters (`pass_ticket`, `uin`, `key`) that are bound to a logged-in user. Direct browser requests are blocked by CORS policies. The Nitro server acts as a proxy that forwards these authenticated requests server-side and returns clean JSON to the client.

### How does the system handle articles with thousands of comments?

The `Downloader` class implements pagination logic that checks the `continue_flag` field in each API response. When `continue_flag` equals `true`, `fetchComments()` automatically requests the next page until all comments are retrieved. For replies, it uses the `max_reply_id` parameter to paginate through threads that exceed 100 replies.

### What is the difference between the comment and comment_reply stores?

The `comment` store (defined in [`store/v2/comment.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/store/v2/comment.ts)) persists the top-level `CommentResponse` objects containing the original user messages. The `comment_reply` store (defined in [`store/v2/comment_reply.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/store/v2/comment_reply.ts)) holds the nested reply objects fetched via the *getcommentreply* endpoint. This separation allows the UI to lazy-load replies only when users expand a specific comment thread.

### How are failed comment download requests handled?

Each API call is wrapped in a try/catch block within the `Downloader` methods. The system uses a `maxRetries` configuration from [`utils/download/constants.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/utils/download/constants.ts) and leverages [`ProxyManager.ts`](https://github.com/wechat-article/wechat-article-exporter/blob/main/ProxyManager.ts) to track proxy health. When a request fails, the downloader automatically selects the next available proxy and retries the operation before surfacing a permanent error to the UI.