# V2EX API scraper — live topics with full post bodies, complete reply threads, member profiles, the 1364-node taxonomy, and full-text search across every V2EX topic since 2010. China's biggest developer/tech community (v2ex.com) in clean JSON. No login, no token, no China exit required.

> Full-text search across EVERY V2EX topic since 2010 (title + body + replies) — the historical archive the site's own JSON API does not expose. Filter by node, date range and sort by recency or relevance; each hit returns the full post body, author handle, node, reply count and markup-free match snippets.
> ReefAPI engine `v2ex` · 8 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/v2ex/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /v2ex/v1/search — 1 credit
Full-text search across EVERY V2EX topic since 2010 (title + body + replies) — the historical archive the site's own JSON API does not expose. Filter by node, date range and sort by recency or relevance; each hit returns the full post body, author handle, node, reply count and markup-free match snippets.

**Parameters:**
- `q` (string, required) — Full-text search term, Chinese or English — e.g. '爬虫' (web scraping, 12.8k topics), '求推荐' (asking for a recommendation, 28.2k), '外包' (outsourcing, 20.4k), '数据采集' (data collection), 'API 接口'. Searches title + body + replies of every V2EX topic since 2010. Quotes are NOT sent upstream (the index rejects them with a 503) — quoting a term switches on exact-phrase matching instead, same as match=phrase.
- `match` (enum, optional, default "any") — How strictly a hit must match a multi-word query. The index does not insist on every token (measured: only 6 of 30 rows returned for '树莓派 教程' contained 教程), so 'all' and 'phrase' narrow the returned rows here and report how many were dropped in `filtered_out`. 'phrase' matches the query as a literal substring, spaces included. [one of: any, all, phrase]
- `sort` (enum, optional, default "created") — Result ordering field. [one of: created, sumup]
- `order` (enum, optional, default "desc") — Sort direction. With sort=created, 'desc' gives the freshest topics first and 'asc' walks the archive from 2010 forward. [one of: desc, asc]
- `node` (string, optional) — Restrict the search to one node slug (e.g. 'jobs' cut a 20,456-hit '外包' search to 2,478 — the filter genuinely bites).
- `since` (string, optional) — Only topics created on/after this date. Accepts 'YYYY-MM-DD', an ISO timestamp, or epoch seconds (milliseconds are converted).
- `until` (string, optional) — Only topics created on/before this date. Same formats as `since`.
- `limit` (integer, optional, default 20) — How many topics to return (1–50, default 20).
- `offset` (integer, optional, default 0) — Pagination offset. The index caps the window at offset + limit ≤ 1000; deeper paging is refused upstream, so narrow the query with `node`/`since`/`until` instead of paging past it.
- `resolve_nodes` (boolean, optional, default true) — The search index stores numeric node ids only. When true (default) the engine attaches the node slug/title from a 24h-cached copy of the node directory; set false to skip that lookup entirely.

**Returns:** topics[]{id, title, content, url, node{id,name,title,title_en,url}, author{username,url}, reply_count, created_at, created_at_iso, match_snippets[], score}, total_matches_upstream, returned, offset, filtered_out, query

**Example request body:**
```json
{
  "q": "爬虫",
  "limit": 10
}
```

### POST /v2ex/v1/node_topics — 1 credit
The newest topics of one V2EX node (sub-forum) with the COMPLETE post body — a live freshness feed for a niche: 'jobs' for hiring posts, 'outsourcing' for project leads, 'qna' for buying-intent questions, 'programmer' for developer chatter. Returns the node's own metadata (topic count, subscriber count) alongside.

**Parameters:**
- `node_name` (string, required) — V2EX node (sub-forum) slug — e.g. 'qna' (问与答, 240k topics), 'programmer' (程序员, 72k), 'jobs' (酷工作, 60k), 'outsourcing' (外包, 7.5k), 'create', 'career', 'python'. Use the `nodes` action to browse or search all 1364 node slugs.
- `limit` (integer, optional, default 10) — How many topics to return (1–10, default 10).

**Returns:** node{id,name,title,title_en,url,topic_count,subscriber_count,parent_node}, topics[]{id, title, content, url, node, author{id,username,url,tagline}, reply_count, created_at, created_at_iso, last_touched_at, last_reply_by}, returned

**Example request body:**
```json
{
  "node_name": "qna"
}
```

### POST /v2ex/v1/latest — 1 credit
The newest topics across the WHOLE of V2EX (all nodes), each with its full post body — a single-call firehose of what the Chinese dev community is posting right now (typically ~47 topics, the freshest usually minutes old).

**Parameters:**
- `limit` (integer, optional, default 50) — How many topics to return (1–50, default 50).

**Returns:** topics[]{id, title, content, url, node, author, reply_count, created_at, created_at_iso, last_touched_at}, returned

**Example request body:**
```json
{
  "limit": 20
}
```

### POST /v2ex/v1/hot — 1 credit
Today's most-discussed V2EX topics with full bodies — the site's own hot list, ordered by the traction the community itself gave them.

**Parameters:**
- `limit` (integer, optional, default 20) — How many topics to return (1–20, default 20).

**Returns:** topics[]{id, title, content, url, node, author, reply_count, created_at, created_at_iso}, returned

### POST /v2ex/v1/topic — 1 credit
One V2EX topic by id or URL: the full post body plus the COMPLETE reply thread (every reply, in order, with author handle and timestamp) — the discussion is where the buying intent, the tool recommendations and the salary numbers actually live.

**Parameters:**
- `id` (string, required) — The topic to fetch: a numeric topic id (1236386) or a topic URL (https://www.v2ex.com/t/1236386#reply7).
- `with_replies` (boolean, optional, default true) — Also fetch the discussion thread (one extra upstream call). The reply endpoint returns EVERY reply in one shot — verified 204/204 on a 204-reply topic — so nothing is silently truncated upstream.
- `replies_limit` (integer, optional, default 100) — Cap on returned replies (1–1000, default 100). `reply_count` always reports the true total, so truncation is visible.

**Returns:** topic{id, title, content, url, node, author, reply_count, created_at, created_at_iso, last_touched_at}, replies[]{id, content, author, created_at, created_at_iso}, replies_returned, replies_total, replies_truncated, replies_match_counter

### POST /v2ex/v1/user_topics — 1 credit
The newest topics posted by one V2EX member, with full bodies — follow a recruiter, a vendor or a prolific poster without watching every node.

**Parameters:**
- `username` (string, required) — V2EX member handle (case-sensitive, e.g. 'Livid'). A numeric member id also works.
- `limit` (integer, optional, default 10) — How many topics to return (1–10, default 10).

**Returns:** author{id,username,url,tagline}, topics[]{id, title, content, url, node, reply_count, created_at, created_at_iso}, returned

**Example request body:**
```json
{
  "username": "Livid"
}
```

### POST /v2ex/v1/member — 1 credit
Public profile of a V2EX member by handle or id: tagline, bio, website, github/twitter handle, self-typed location, join date and PRO status. Only what the member published themselves — the API exposes no e-mail, phone or real name.

**Parameters:**
- `username` (string, required) — V2EX member handle (case-sensitive, e.g. 'Livid'). A numeric member id also works.

**Returns:** member{id, username, url, tagline, bio, website, github, twitter, location, avatar, joined_at, joined_at_iso, is_pro}

**Example request body:**
```json
{
  "username": "Livid"
}
```

### POST /v2ex/v1/nodes — 1 credit
The V2EX node taxonomy — all 1364 sub-forums with slug, Chinese and English title, topic count, subscriber count and parent node. Pass `name` for one node, or `query` to search the directory (matches slug, Chinese title and English title). This is how you find the `node_name` for `node_topics` and the `node` filter for `search`.

**Parameters:**
- `name` (string, optional) — Fetch exactly one node by slug (cheapest call — ~650 bytes instead of the full directory).
- `query` (string, optional) — Search the node directory by slug or title (Chinese or English), e.g. 'python', '工作', 'job'.
- `limit` (integer, optional, default 200) — How many nodes to return (1–1400, default 200).

**Returns:** nodes[]{id, name, title, title_en, url, topic_count, subscriber_count, parent_node, aliases[]}, returned, total_nodes

**Example request body:**
```json
{
  "query": "python"
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=v2ex
- Human docs page: https://reefapi.com/docs/v2ex
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
