Our own visit statistics are always on. Google Analytics, which helps us understand how people use our public pages (not your signed-in workspace), is enabled only with your permission. See our Privacy policy.

Claude Code dynamic workflows for Xiaohongshu comments: API test and script example

Claude Code dynamic workflows sort Xiaohongshu comments in parallel. Our test: 4 of 8 pages had 10 rows, one post was 96% noise, 500 comments cost $0.222.

Illustration: at the back, a tall unrolled paper scroll covered only with wavy lines stands for the workflow script. In front, a conveyor belt carries many small comment bubbles. Five cube-shaped robots pick bubbles and drop them into a row of sorting bins. One bin is teal. A waste basket on the right catches crumpled paper, which stands for the noise that the script removes first.

On Nov 2, 2026, from 00:14 to 00:22 Singapore time (SGT), we made 10 billed calls to the EveryInfra API. They returned 100 top-level comments from 6 Xiaohongshu (RedNote) posts about robot vacuums, for $0.066671 in total. On one post, 24 of 25 comments said only "join the group", "count me in" or "join" in Chinese. The catalog says xiaohongshu/comments returns 15 rows per page, but 4 of our 8 pages returned 10.

That changes the budget for a Claude Code dynamic workflow. At the catalog page size, 500 comments need 34 calls and cost $0.189. At our measured mean of 12.5 rows per page, they need 40 calls and cost $0.222. If you also drop the 25% noise and still want 500 useful comments, you need 54 calls and $0.300.

A fourth result matters for reruns. We sent the same request three times, and the first page changed by 7 comments in 6 minutes. Each of the three calls was billed.

This article answers one practical question. You have a product keyword and you want Claude Code to write a parallel program. The program pulls Xiaohongshu comments, sorts them into four groups (need, complaint, price, praise) and writes one summary table. Which feature do you use, what does the script look like, and how do you connect the data?

How many calls does it take for 500 comments, and what breaks under rate limits and retries? Every Claude Code fact below comes from the Claude Code workflows page or the changelog. We did not run a workflow on our machine, so the Claude Code parts are configuration examples. Every EveryInfra number comes from a raw API response.

Illustration in two halves. On the left, five cube robots hand cards to one desk until the notebook overflows and cards fall on the floor, which shows subagent results that all land in one context. On the right, each robot drops one card into its own drawer next to an unrolled script, and only one small teal card travels along a dotted path to a tidy desk.
Subagents return results to the conversation. A workflow keeps them in script variables and returns one final answer. Illustration generated for this article (rolldek gpt-image-2.5), based on the Claude Code docs

The job: 500 Xiaohongshu comments, four groups, one table

We define the job first, because every number later depends on it. The input is one keyword. We used the Chinese word for "robot vacuum", because the category has many "which one should I buy" and "this broke" threads around the November 11 sales. The output is one table. It shows the count and share of each group and three example comments per group. It also shows the API cost and the agreement rate from a spot check.

Three stages do the work. xiaohongshu/search finds posts. xiaohongshu/comments and xiaohongshu/sub_comments pull comments and replies. A model then labels each comment. The script defines the four groups.

A need asks which model to buy or asks for a link. A complaint reports a problem with a product or a service. A price comment talks about cost, cheaper copies or where to buy. Praise comes from a person who uses the product and recommends it. All other comments go to "other".

Why use a parallel program for this job? It meets three conditions. First, the posts are independent, so post A does not need the result from post B. Second, the input is much larger than the output. One comment returns about 1,445 bytes of JSON, but its text is about 95 bytes. Third, the labeling rule is fixed, so a small model can do it.

Anthropic published a guide on Oct 22, 2026 that asks most agent builders to use fewer subagents (Anthropic article). The same guide keeps subagents for one case. That case is several tasks that need fresh, separate contexts in parallel. Comment sorting is that case.

We pulled 6 posts. First we searched with sort=most_commented and got 20 posts. Then we picked 6 posts with different topics. The table below shows them with English summaries of their titles. Ten of the 20 search results had is_cooperation_note set to true. On Xiaohongshu, these are sponsored posts that go through the Pugongying platform.

That platform reviews a sponsored post before it goes live. The brand must accept or reject it within 72 hours (Pugongying help center, page dated 2026-10-22; Pugongying home). None of our 6 posts was sponsored. Keep that in mind when you read the praise numbers.

Post (our code)Title, summarized in EnglishComments shown in searchTop-level comments we pulled
A"Do not buy a robot vacuum yet, see the Singles' Day test first"2,06925 (2 pages)
BA complaint thread about a Roborock vacuum57030 (2 pages)
C"2026 robot vacuum buying guide"37710
DA short comedy video about a "strong" vacuum3,27810
E"A waste-of-money appliance: the useless robot vacuum"97315
FA first-time buyer asks for advice48710

What Claude Code dynamic workflows are, in the words of the docs

The Claude Code docs define a dynamic workflow as a JavaScript script that orchestrates many subagents at once. Claude writes the script for your task. A runtime executes it in the background while your session stays responsive (workflows page). The feature first shipped in v2.1.154 on May 28, 2026. The changelog says that Claude "orchestrates work across tens to hundreds of agents in the background" and that /workflows shows your runs (changelog).

The launch article now says that dynamic workflows are generally available. It lists the CLI, the Desktop app and the VS Code extension. It lists Pro, Max, Ultra, Team and Enterprise plans, plus the Claude API, Amazon Bedrock, Vertex AI and Microsoft Foundry (Anthropic launch article).

The docs show a saved script. It starts with export const meta = { name, description }. The body is plain JavaScript with top-level await. agent() starts one subagent.

pipeline() runs one chain per item in a list. parallel() runs a set of tasks at the same time and waits for all of them. phase() groups the next agents under a title, log() prints a line, and args holds the input (workflows page, "What the saved script looks like").

When you pass a schema to agent(), the subagent returns an object that matches that JSON Schema. If the output fails validation five times, the call fails. MAX_STRUCTURED_OUTPUT_RETRIES changes that count (same page). This is exactly what the labeling stage needs. Each labeling agent returns an array of {id, label} objects, and the script counts them directly.

There are four ways to start a workflow (workflows page, "Have Claude write a workflow"; commands page).

How you start itWho decides to use a workflowNote
Type the keyword ultracode in the promptYou, for this promptOnly in a prompt that you type
Ask for "a workflow" in your own wordsYou, for this promptClaude writes the script for this task
Run /effort ultracode or claude --effort ultracodeClaude, for each taskThe flag also sets effort to xhigh
Run a saved workflow command, such as /deep-researchYouReuses a run that worked

The keyword works only in a prompt that a person types. It does not work in a prompt passed with -p (headless docs). It also does not work in a scheduled task prompt, a webhook or a pull request comment. So for a weekly comment report, save one good run as a command first. Then call that command from the scheduled task.

SubagentsSkillsAgent teamsWorkflows
What it isA worker Claude startsInstructions Claude followsA lead agent with peer sessionsA script the runtime runs
Who decides the next stepClaude, turn by turnClaude, from the promptThe lead agentThe script
Where results liveClaude's contextClaude's contextA shared task listScript variables
What you can repeatThe worker definitionThe instructionsThe team definitionThe orchestration
ScaleA few tasks per turnSame as subagentsA few long-running peersDozens to hundreds of agents
After an interruptionThe turn restartsThe turn restartsTeammates keep runningResumable in the same session

The table follows the comparison in the "When to use a workflow" section of the workflows page. The third row matters most here. With subagents, each post summary lands in the main conversation. With a workflow, the 100 comments, the labels and the spot-check results stay in script variables.

The conversation gets only the final table. Agent teams are still experimental and need CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (agent teams docs), so we do not use them here.

Dynamic workflows vs subagents: the plan lives in code, not in the chat

The real difference is not parallelism. The difference is who holds the plan. With subagents, Claude decides each turn whom to start next. Every result must come back to its context before it can decide. The subagent docs say that at 20 running subagents, the next Agent call fails with an error that tells Claude not to retry. CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS changes that number (subagents docs, "Concurrent subagent limit").

With a workflow, the loops, branches and retry counts are fixed in the script. Claude does not decide again for each post, so early results cannot change how later posts are handled. For labeling, that is a good property. Every post gets the same prompt and the same schema, so you can compare the results across posts.

The runtime has hard limits. The screenshot below is from the Claude Code docs. The teal box marks the three limits that matter most here. By default, up to 16 agents run at once, and fewer on machines with fewer CPUs.

CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS sets a value from 1 to 256, from v2.1.269. One parallel() or pipeline() call accepts up to 4,096 items and rejects a longer list with an error. One run starts at most 1,000 agents.

Three other limits also matter. A run cannot ask you for input in the middle. The script cannot read files or run shell commands by itself. A script that contains import() fails before it starts.

Screenshot of the "Behavior and limits" table on the Claude Code workflows docs page, with constraints on the left and reasons on the right. A teal box marks three rows: up to 16 concurrent agents by default, changeable from 1 to 256 with CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS; agents with the same prompt-cache prefix start up to 5 seconds later; up to 4,096 items per parallel or pipeline call and 1,000 agents per run.
The runtime limits table in the Claude Code docs, read on Nov 2, 2026. Screenshot of the Claude Code workflows docs

The rule that the script cannot touch files or commands decides how data comes in. The script cannot call fetch or curl. So the agents must call EveryInfra. An agent can use the MCP tools that the session has connected. It can also run Bash if your permissions allow it.

The bundled workflow-authoring reference adds one more fact. Workflow agents can reach every MCP tool that the session connects, but servers that need an interactive sign-in can be missing in headless runs. That reference is the /workflow-authoring skill, which needs v2.1.248 or later (workflows page, "Edit a saved script"; skills docs). The EveryInfra remote MCP server uses a key in a request header, not an interactive sign-in. So it also works in headless runs.

One more rule is about time. Inside the script, Date.now(), Math.random() and new Date() without an argument throw an error. A resumed run must repeat the same agent() calls, and timestamps or random values would break that (workflows page). If you need a date, pass it in through args. For "comments from the last 24 hours", use the API filter instead. xiaohongshu/search accepts date_range=last_24_hours.

Versions and switches: which behavior starts in which release

Dynamic workflows changed a lot between May and November 2026. Many tutorials describe old behavior. The table lists only the entries that matter for this job. Versions and dates come from the Claude Code changelog, which mirrors the CHANGELOG.md on GitHub. Rows marked "docs" come from version notes on the workflows page.

VersionDateChangeEffect on comment sorting
2.1.1542026-05-28Dynamic workflows and /workflows shipStarting point
2.1.1572026-05-29A setting turns off the keyword triggerTyping "workflow" no longer starts a run by accident
2.1.269docsCLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTSLimit API-calling agents to 2 or 3
2.1.271docsRuns pause at a usage limit; Pro default size is smallLong runs do not fail halfway
2.1.2962026-10-09CLAUDE_CODE_WORKFLOW_SUBAGENT_MODELOne variable puts every agent on Haiku
2.1.2992026-10-14/usage warns about workflowsYou can see where tokens went
2.1.3012026-10-15Fix: agents skipped a step and the run still reported successA skipped labeling step no longer looks like success
2.1.3052026-10-20Fix: resume reran completed pipeline() and parallel() agentsResume no longer pays twice for comments
2.1.3112026-10-26Fix: subagents missed MCP servers that connected lateA slow remote MCP connect no longer hides tools
2.1.3162026-10-31CLAUDE_CODE_SUBAGENT_EFFORTSet low effort for all labeling agents at once

The 2.1.269 and 2.1.271 rows come from the "Behavior and limits", "When a run hits your usage limit" and "Set a size guideline" sections of the workflows page. The size guideline has four values. unrestricted means no guideline, small means fewer than 5 agents, medium means fewer than 10, and large means fewer than 50. Claude treats the value as advice, not as a cap. The hard cap is still 1,000 agents. You can also set workflowSizeGuideline in any settings file (settings reference).

For 500 comments, medium is enough. You need 1 search agent, a few fetch agents, the same number of labeling agents, 1 spot-check agent and 1 report agent. A later section shows how a batch call cuts the fetch agents to 1 or 2.

There are two ways to turn on ultracode, and they have different effects. In a session, /effort ultracode turns on ultracode only and does not change the effort level, from v2.1.284. /effort ultracode off turns it off.

Before v2.1.284, /effort ultracode set the session to xhigh effort, and /effort ultracode off failed with Invalid argument. The launch flag claude --effort ultracode (v2.1.203 or later) works in a different way. It turns on ultracode and also sets effort to xhigh (model configuration docs; workflows page). With ultracode on, the "Large workflow" warning does not appear and the subagent concurrency limit is not enforced. For jobs that call a paid API, we suggest that you keep ultracode off. Ask for a workflow in one prompt instead, so that you see the plan before it runs.

Connect the data: remote MCP or POST /api/v1/social

A workflow can use the data API in two ways. An agent can call EveryInfra as a remote MCP server, or it can run curl against POST https://api.everyinfra.com/api/v1/social. The Claude Code MCP docs add a remote HTTP server with claude mcp add --transport http <name> <url>. For Bearer auth, you add --header (MCP docs).

Our remote MCP server uses the Streamable HTTP transport from the MCP spec (MCP spec 2025-11-25, transports). In the spec, a tool call is a tools/call request, and the result is in a content array (MCP spec 2025-11-25, tools). Our tools/list call returned 12 tools. Three of them matter here: everyinfra_list_capabilities, everyinfra_call_api and everyinfra_get_job.

bash
# Configuration example. Keep the key in an environment variable, never in a file.
export EVERYINFRA_API_KEY="<your key>"
claude mcp add --transport http everyinfra https://api.everyinfra.com/mcp \
  --header "Authorization: Bearer ${EVERYINFRA_API_KEY}"

# Fewer approval prompts: add these to permissions.allow in .claude/settings.json
#   "Workflow", "mcp__everyinfra__everyinfra_call_api", "mcp__everyinfra__everyinfra_get_job"
# Keep at most 3 agents running, so a 10-per-minute key is not saturated
export CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS=3

The permission lines have three sources. In claude -p and in the Agent SDK, Claude Code never shows the workflow approval prompt. To let a run start, add Workflow to your allow rules. For one saved workflow, add Workflow(<name>) (workflows page, "Approve the plan before it runs").

The launch article suggests auto mode for workflows. In auto mode, a classifier reviews each call (permission modes docs). A PreToolUse hook can also return allow for the call (hooks docs).

MCP allow rules use the form mcp__<server>__<tool>. A glob is valid only after the server name, as in mcp__everyinfra__* (permissions docs). The subagents of a workflow use your permission rules. The docs suggest that you allow the tools that agents need before a long run, so the run does not stop for approvals.

Which method should you use? For read-only labeling with a known number of calls, we use MCP. The table compares the two methods. The sandbox row comes from the sandboxing docs, which start with no allowed network domains.

QuestionRemote MCPcurl to `POST /api/v1/social`
Does the agent need Bash?NoYes, plus a sandbox network rule
Does the agent know billing and paging?Yes, from the tool descriptionOnly from your prompt
Error fields such as retry_after and did_you_meanIn the tool resultIn the response body
Raw JSON on disk for an auditNoYes, the agent writes the file
Best forLabeling with a known call countJobs that must keep every raw file

With curl, the fetch agent writes each raw file and returns only {id, text, like_count} to the script.

Know one size limit before you start. Claude Code warns when an MCP tool output passes 10,000 tokens and caps it at 25,000 tokens by default. MAX_MCP_OUTPUT_TOKENS changes the cap. A successful text result longer than 50,000 characters goes to a file (MCP docs, "MCP output limits and warnings").

One page of 15 comments over MCP was 16,892 bytes. One search page of 20 posts was 34,991 bytes. Both are below the line.

A comments_batch call for 3 posts was 42,991 bytes. At that rate, 20 posts give about 280,000 bytes. That result will go to a file, and the agent must read the file in a second step. This does not change the result. It only adds a step. To keep the result inside the agent's context, request 3 or 4 posts per call.

One real round trip: what the agent sends and what the API returns

At 00:17:55 SGT on Nov 2, 2026, we sent the tools/call request below over MCP. It is the same JSON-RPC request that a Claude Code agent sends when it calls mcp__everyinfra__everyinfra_call_api. The target was post F, the first-time buyer, sorted by likes. The result sits in result.content[0].text as JSON text. We show the first comment, the billing field and nothing else. We removed the query string from the post URL, and author names and IDs are hidden.

json
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"everyinfra_call_api",
 "arguments":{"platform":"xiaohongshu","action":"comments",
  "params":{"url":"https://www.xiaohongshu.com/explore/692b0d81000000001d03ea4c","sort":"most_liked"}}}}

{"id":"req_b7efc0a2e4784211","platform":"xiaohongshu","action":"comments","count":10,
 "pagination":{"page":1,"has_more":true,"stop_reason":null},
 "results":[{"id":"693ab483000000001702fef1","text":"(Chinese) How is the DJI one? I hear its obstacle avoidance is strong",
   "like_count":3,"author_name":"[hidden]","author_id":"[hidden]","sub_comment_count":0}],
 "billing":{"charged":true,"charged_usd":0.005556}}

Eight of these 10 comments are clear needs. Almost every one gives the floor area, a budget, pets and the floor type. One example, translated: "140 square meters, carpets, no pets, tiles everywhere, lots of hair, budget 3,000 yuan, which one?" This type of comment is useful for product and support teams. It is also well structured, so a model can extract budget, area and pets as fields.

The returned fields match the 20 response_fields for xiaohongshu/comments in the catalog. The table shows which ones the fetch agent keeps.

FieldWhat it holdsKeep for labeling?
id, text, like_countComment ID, text and likesYes
sub_comment_countNumber of repliesYes, to pick threads for replies
comment_tags"Liked by the author", "pinned", poll answersOptional, helps the noise filter
posted_at, ip_locationTime and IP regionOnly for time or region reports
author_name, author_id, avatarWho wrote itNo, and do not put them in reports
note_url, next_page_token, sub_commentsPaging and the first replyOnly inside the fetch agent
Screenshot of a real EveryInfra response for the first comment page of post A. The top line shows the request URL, a body summary, 2026-11-02 00:15:22 SGT and HTTP 200. Teal boxes mark count 10, four comment texts that each mean "join the group", and charged_usd 0.005556 in billing. Author names and IDs are hidden.
The real first page of post A: 10 rows, the first 4 are "join the group" messages, and the call is billed once. Screenshot of an EveryInfra API response; capability details at

The script skeleton: discover, fetch, filter, label, check, report (example)

We wrote the skeleton below from the script API in the docs. We did not run it, so treat it as a configuration example. It has five phase() calls but six stops. The filter is not an agent. It is one regex in the script, and it costs no tokens.

Each design choice follows from a measurement in this article. Fetch agents return only id, text and like_count, not author data and 30 other fields. Labeling agents use a schema with five fixed labels. The spot check uses a different model to relabel 10% of the comments, and it does not see the first labels.

Flow animation. A dashed box at the top shows one-time setup: claude mcp add for EveryInfra, allow Workflow and mcp__everyinfra__*, keep the key in an environment variable. Six cards light up in order: 1 Discover, 1 agent calls xiaohongshu/search; 2 Fetch, 1 agent per post calls comments; 3 Pre-filter, a regex in the script with 0 agents; 4 Classify, 1 agent per post with a schema; 5 Spot-check, a second agent relabels 10%; 6 Report, 1 agent writes the table. The center text says API cost for 500 comments is about $0.222 for 40 calls, and Claude tokens are extra.
javascript
export const meta = {
  name: 'xhs-comment-triage',
  description: 'Pull Xiaohongshu comments for a keyword, drop noise, label need/complaint/price/praise, write a table',
  whenToUse: 'You have a product keyword and want to know what the comment sections ask and complain about',
  phases: [
    { title: 'Discover', detail: '1 agent searches posts' },
    { title: 'Fetch', detail: '1 agent per post pulls 2 pages' },
    { title: 'Classify', detail: '1 agent per post labels comments' },
    { title: 'Verify', detail: 'another model relabels 10%' },
    { title: 'Report', detail: '1 agent writes the table' },
  ],
}

const KEYWORD = (args && args.keyword) || 'robot vacuum (Chinese keyword)'
const NOTE_COUNT = (args && args.notes) || 20
const TOOL = 'mcp__everyinfra__everyinfra_call_api'
const LABELS = ['need', 'complaint', 'price', 'praise', 'other']
const NOISE = /^(进团|参团|进|\s*)$|^(\[[^\]]+R\]|\s)*$/   // "join the group" replies, empty text, emoji only

const NOTES = { type: 'object', required: ['notes'], properties: { notes: { type: 'array', items: {
  type: 'object', required: ['url', 'title'],
  properties: { url: { type: 'string' }, title: { type: 'string' }, comment_count: { type: 'integer' } } } } } }
const COMMENTS = { type: 'object', required: ['comments', 'calls', 'charged_usd'], properties: {
  calls: { type: 'integer' }, charged_usd: { type: 'number' },
  comments: { type: 'array', items: { type: 'object', required: ['id', 'text'],
    properties: { id: { type: 'string' }, text: { type: 'string' }, like_count: { type: 'integer' } } } } } }
const LABELED = { type: 'object', required: ['items'], properties: { items: { type: 'array', items: {
  type: 'object', required: ['id', 'label'],
  properties: { id: { type: 'string' }, label: { type: 'string', enum: LABELS } } } } } }

phase('Discover')
const found = await agent(
  `Use ${TOOL} with platform=xiaohongshu, action=search, params={keyword:"${KEYWORD}", sort:"most_commented"}. ` +
  `Follow next_page_token until you have ${NOTE_COUNT} posts. Keep each url exactly as returned, with xsec_token.`,
  { schema: NOTES, model: 'haiku', effort: 'low', label: 'search' })

const results = await pipeline(found.notes.slice(0, NOTE_COUNT),
  (note) => agent(
    `Use ${TOOL} for xiaohongshu/comments with params={url:"${note.url}"}, then fetch page 2 with next_page_token. ` +
    `On 429, wait error.retry_after seconds and retry, at most 3 times. On 422, do not retry; report it. ` +
    `Keep only id, text and like_count. Sum billing.charged_usd into charged_usd.`,
    { schema: COMMENTS, model: 'haiku', effort: 'low', phase: 'Fetch', label: note.title.slice(0, 12) }),
  (r) => ({ ...r, kept: r.comments.filter((c) => !NOISE.test(c.text.trim())),
            dropped: r.comments.filter((c) => NOISE.test(c.text.trim())).length }),
  (r) => agent(
    `Label each Xiaohongshu comment as one of ${LABELS.join(', ')}. need = asks which model to buy or for a link; ` +
    `complaint = a product or service problem; price = cost, cheaper copies or where to buy; ` +
    `praise = the writer uses it and recommends it; other = jokes or off-topic.\n` +
    JSON.stringify(r.kept.map((c) => ({ id: c.id, text: c.text }))),
    { schema: LABELED, model: 'haiku', effort: 'low', phase: 'Classify', label: 'classify' })
    .then((l) => ({ ...r, labels: l ? l.items : [] })),
)

const ok = results.filter(Boolean)
const all = ok.flatMap((r) => r.labels.map((x) => ({ ...x, text: (r.kept.find((c) => c.id === x.id) || {}).text })))
const sample = all.filter((_, i) => i % 10 === 0)
log(`Fetched ${ok.length}/${found.notes.length} posts; filter dropped ${ok.reduce((s, r) => s + r.dropped, 0)} comments`)

phase('Verify')
const second = await agent(
  `Label these comments on your own. Labels: ${LABELS.join(', ')}.\n` + JSON.stringify(sample.map(({ id, text }) => ({ id, text }))),
  { schema: LABELED, model: 'sonnet', effort: 'medium', label: 'relabel-10pct' })
const agree = sample.filter((s) => ((second && second.items.find((y) => y.id === s.id)) || {}).label === s.label).length

phase('Report')
const counts = {}
for (const x of all) counts[x.label] = (counts[x.label] || 0) + 1
return await agent(
  `Write a Markdown table with count and share per label and 3 example comments per label, translated to English. ` +
  `End with API cost $${ok.reduce((s, r) => s + r.charged_usd, 0).toFixed(4)} and spot-check agreement ${agree}/${sample.length}.\n` +
  JSON.stringify({ counts, examples: all.slice(0, 150) }),
  { model: 'sonnet', effort: 'low', label: 'report' })

Some parts of this skeleton are easy to get wrong, and the docs cover each of them. meta must be the first statement and a plain object literal. If it contains a variable, a function call or a spread, /<name> drops out of autocomplete (workflows page, "Edit a saved script"). The titles in phases must match the phase() arguments exactly, or the progress view gets an extra group.

When one agent inside pipeline() fails, its slot becomes null, so the script calls filter(Boolean). When an agent that you await directly fails, the whole run ends ("When an agent stalls and restarts", same page). The fetch and label stages use haiku. The check and the report use sonnet.

Claude Code picks a model for workflow agents in the same order as for subagents. A model named in the script is the per-call model. With no model, the agent uses the session model (workflows page, "Cost"; the subagents docs list the aliases sonnet, opus, haiku and fable).

Why does the filter live in the script and not in a model? The script is plain JavaScript, so a regex, a filter and a count cost no tokens. The filter removes "join the group" replies, empty text and emoji-only comments. None of these needs understanding.

The next section shows that they were 25 of our 100 comments. The bundled authoring reference also says that a workflow should log() what it drops when it limits coverage. Without that line, a reader assumes full coverage. The log() line in the skeleton does that job.

Test 1: the catalog says 15 rows per page; 4 of 8 pages had 10

For xiaohongshu/comments, the catalog says page_size is 15 and limit_ignored is true. So the page size is fixed and a limit value has no effect. xiaohongshu/sub_comments has 10, xiaohongshu/search has 20, and comments_batch has 15 per post. Before the tests, we wrote down the hypothesis "every page has 15 rows". The data rejected it.

At 00:15:22 SGT, the first page of post A had 10 rows, and its second page at 00:15:49 had 15. At 00:17:38, one comments_batch call for 3 posts returned 10 rows for C, 10 for D and 15 for E. At 00:17:55, post F over MCP returned 10. The mean of the 8 pages is 12.5 rows.

Horizontal bar chart of rows returned per comment page. Post A page 1 has 10 and page 2 has 15. Post B pages 1 and 2 have 15 each. Post F page 1 has 10. Posts C and D page 1 have 10 each. Post E page 1 has 15. A dashed line marks the catalog value of 15. Four of the 8 pages returned 10 rows, and the mean is 12.5.

A short page does not mean the end. For posts A and F, pagination.has_more was true on the short page. For posts C and D in the batch result, every row had a next_page_token. Page 2 of post A was back to 15 rows. We guess that the platform treats the first page differently, for example for pinned or poll comments. We have no more evidence, so we report only what we saw.

The practical point is about the stop rule. With a variable page size, "target count divided by 15" does not give the call count. The correct stop rule is "I have enough rows" or "has_more is false". It is not "I fetched N pages".

Replies behave the same way. One comment on post B had sub_comment_count 52. At 00:17:17 SGT we called xiaohongshu/sub_comments for it. Page 1 returned 5 rows, not 10, and 2 of them had empty text, probably images or stickers.

So replies cost more per useful row. A top-level page gave 12.5 rows on average for $0.005556, but this reply page gave 3 rows with text. For labeling, we suggest that you skip replies by default. Pull them only for a comment that the model marks as a complaint and that has many replies.

CapabilityCatalog page_sizeRows per page we sawPrice per callCost per useful row
xiaohongshu/search2020 (1 page)$0.005556about $0.00028 per post
xiaohongshu/comments1510 to 15, mean 12.5 (8 pages)$0.005556about $0.00044 per comment
xiaohongshu/comments_batch15 per post10 to 15 per post (3 posts)$0.005556 per postsame as a single post
xiaohongshu/sub_comments105, of which 2 had no text (1 page)$0.005556about $0.0019 per reply

Test 2: what a rule filter removes, and what it cannot do

Our third hypothesis was "comments can go straight to the model". The pass mark was less than 30% noise on every post. Post A failed it badly. Of its 25 comments on two pages, 24 said only "join the group" (17), "count me in" (6) or "join" (1) in Chinese. The screenshot shows that most of them carry the "liked by the author" tag, and some carry a poll answer tag. Each has sub_comment_count 1.

Our reading is that the author organized a group buy in the comments, and users replied to be added. These comments say nothing about what users want, but they fill billed pages. Among the other 5 posts, only post D had 1 noise row, which was an empty comment.

Stacked bar chart of rule-based labels for each of 6 posts. A Singles' Day test: noise 24, need 1. B Roborock rant: need 6, complaint 7, price 7, undecided 10. C Buying guide: need 4, complaint 1, undecided 5. D Strength skit: noise 1, need 2, undecided 7. E Waste of money: need 3, complaint 4, praise 5, undecided 3. F First-time buyer: need 8, complaint 1, price 1. Teal marks noise that the script can drop.

To see what one regex can do, we wrote a keyword rule classifier as a baseline. First the rule marks noise: text that is only "join the group", only emoji, or empty. Then it checks complaint words (smell, return, waste of money, tangles), then need words (recommend, which one, question marks), then price words (price, yuan, cheaper copy, budget), then praise words (works well, clean, saves time). A comment that matches nothing is "undecided".

This is not a model result. It shows which parts a rule can do and which parts need a model. Across 100 comments, the rule gave noise 25, need 24, complaint 13, price 8, praise 5 and undecided 25.

The rule got it rightThe rule got it wrong or could not decide
All 25 "join the group" replies matched exactly and were removed"So the ghost hid in our house for four days...?" is a joke, but the question mark made it a need
"3 yuan each, lasts a month" became price"My budget Xiaomi does not tangle hair" is praise, but the word "tangle" made it a complaint
"140 square meters... budget 3,000, which one?" became need"I only learned from the comments that the dust bag is single-use" is new knowledge, so it stayed undecided
"The dock exhaust smells bad, how do I fix it?" became complaint"Do you stand next to the washing machine for 30 minutes?" is rhetorical, but it became a need

The table explains why the script has two stages. The rule only removes noise. Noise looks the same every time, and a wrong drop costs almost nothing. The four intents go to a model, because one question mark can be a request, a rhetorical question or a joke.

Post C has "buying guide" in its title and post D has "robot vacuum" in its title. But their comment sections talk mostly about the creator's comedy plot and fitness. Seven or more of those rows are jokes that both a rule and a model should mark as "other". If you pick posts by title only, you overestimate useful comments. So we suggest that the discover agent also gets is_cooperation_note, the comment count and the title. It can then skip clear comedy posts first.

Research on text labeling with models helps to set the spot check. One study found that zero-shot labels beat crowd workers on most tasks (arXiv 2303.15056). Another found large variation across models and tasks (arXiv 2311.05769), and a methods paper lists rigorous validation as a required step (Sociologica, 2024). For this job, that means three rules. Do not skip the spot check. Use a different model that does not see the first labels. Set a pass mark in advance, for example 80% agreement. If the run is below it, change the label definitions and relabel the whole batch.

Test 3: the same page three times gave 7 new comments, and each call was billed

This test answers a workflow question: what happens when an agent fails and runs again? We fetched the first page of post B at 00:15:33 SGT. We sent the same request body again at 00:21:35 and at 00:21:55. The second response shared only 8 of 15 comments with the first.

The third shared 13 with the first and 10 with the second. Together, the three calls returned 22 distinct comments. Each call cost $0.005556, and the identical request did not make it free.

Stacked bar chart of three fetches of the same first page of post B. At 00:15:33 all 15 comments are new. At 00:21:35, 8 were seen before and 7 are new. At 00:21:55, all 15 were seen before. Three fetches give 22 distinct comments, and each fetch was billed once.

So the default sort is not a stable order. It changes with time and with the platform's ranking. In a workflow, this adds to the resume rules. The docs are clear. On resume, completed agents return their saved results.

But the first agent whose prompt changed runs again, and so does every agent after it. A failed agent runs again, and so does every agent that the script starts after it, even completed ones. The docs give an example. A script starts A, B, C and D, and B fails. On resume, A comes from the cache, and B, C and D run again (workflows page, "Which agents run again").

v2.1.305 on Oct 20, 2026 fixed a related problem. Before that fix, an unedited script on resume could rerun completed agents in pipeline() and parallel() (changelog 2.1.305).

Put the two facts together. When a fetch agent runs again, it calls the API again and you pay again. The comments that come back can differ, so the labels that later agents already made may not match. We suggest three rules outside the skeleton.

First, split fetching and labeling into two workflows. The first workflow only fetches and writes files. When the data looks right, the second workflow reads the files and labels them. A failure in the labeling run then never touches the API.

Second, fetch with sort=newest or sort=most_liked, which are much more stable than the default. Do not change the sort while you page, because the catalog says that an old next_page_token stops working. Third, deduplicate labels by comment id before you count, because one comment can appear in two fetches.

Stalls cause another kind of rerun. If an agent produces no output for long enough, it starts over from the same prompt, up to five times. Files that it already changed stay changed, and its tokens stay in the run total (workflows page, "When an agent stalls and restarts"). Time spent waiting on its own tool calls does not count, so a 2.5-second API call does not trigger a stall.

A different failure happens when a response stream goes silent. A streaming idle watchdog ends it, and CLAUDE_STREAM_IDLE_TIMEOUT_MS sets that timeout (network configuration docs). If an agent waits a long time after a 429 and shows no output, it can be treated as stalled and start again. Then it repeats calls that you already paid for.

Our 429 asked for a 1-second wait, so the risk was small. On a busy shared key, waits get longer. In that case, give fetch agents their own stallMs, for example agent(prompt, { stallMs: 600000 }).

Parallel rate limits: 16 agents against a 10-per-minute key

A workflow runs 16 agents at once by default. Our key has its own limit of 10 requests per minute, and other programs shared the same key at the same time. We called about every 10 seconds, which should be well below the limit. But the x-ratelimit-remaining-requests header read 5, 5, 2 and 0.

At 00:15:59 SGT, a call returned 429. After that the header read 8, 3, 6, 5 and 1. This is what a shared key looks like. You cannot see when others call, so you can only read the header and the 429s. If only your program uses a key, the remaining count should go down with each of your calls and not jump up and down. The jumps here come from the shared key, and you will not see them on a key of your own.

Line chart of the remaining requests header on each response, at the 10 times from 00:14:59 to 00:21:55 that have this header, on a 0 to 10 scale. The 422 at 00:16:07 has no header, so it has no point. The values are 5, 5, 2, 0, 0, 8, 3, 6, 5 and 1. The point at 00:15:59 is teal and marked 429. Other programs shared the same key, so even a call every 10 seconds hit the limit.

An agent can read the 429 body directly. The text was "rate limit exceeded, slow down: 10 requests per minute for this API key's own limit. Retry after 1s.". The body also had retry_after: 1, retryable: true, max_retries: 3 and the sentence "Nothing was charged". We waited more than 60 seconds to be safe and got a 200 at 00:17:17.

The 429 cost nothing, and it is not in our billed total. The fetch prompt in the skeleton uses the same rule: on 429, wait error.retry_after seconds and retry, at most 3 times. The Anthropic API also returns 429 with a retry-after header when you go too fast (Anthropic rate limits docs). One prompt rule can cover both.

json
{"error":{"code":"rate_limited",
  "message":"rate limit exceeded, slow down: 10 requests per minute for this API key's own limit. Retry after 1s.",
  "reason":"key_limit","retry_after":1,"limit":10,"fault":"account","retryable":true,"max_retries":3,
  "next_steps":["Over this key's or account's request rate. Nothing was charged. Retry after retry_after seconds, or ask the user to raise the limit."]}}

Retries are a passive defense. There are two active ones. The first is lower concurrency. Set CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS to 2 or 3.

A fetch agent that pulls two pages needs about 5 seconds of API time plus its own thinking time. With 3 agents, the request rate is close to 10 per minute, and retries cover the rest. This variable applies to the whole run, so it also slows the labeling agents. If total time matters, split fetching and labeling into two workflows, and do not limit the labeling run.

The second active defense is the batch call. xiaohongshu/comments_batch takes up to 20 posts per request and bills per post. At 00:17:38 SGT we fetched 3 posts in one request. It returned 35 comments and cost $0.016667, which is $0.005556 per post, the same as xiaohongshu/comments. The request took 3.28 seconds, and a single-post call took about 2.4 seconds.

First pages of 40 postsRequestsMinimum minutes at 10 per minuteAPI cost
One xiaohongshu/comments call per post404$0.22224
xiaohongshu/comments_batch, 20 posts per call21$0.22224

The batch call changes the request count, not the cost. For a shared key and a workflow, the request count is the real bottleneck. 40 requests on a 10-per-minute key need at least 4 minutes. 2 requests finish in seconds. So a better skeleton uses 1 or 2 agents to call comments_batch for every first page after discovery. Then it pages further only on posts with many comments.

Every row in a batch result has a next_page_token, so you can continue with xiaohongshu/comments. The catalog has one warning: when some posts fail, check partial and missed in the response. Do not treat a missing post as a post without comments. Put that sentence in the fetch prompt.

Async mode is also available. The catalog says that any capability accepts "mode":"async" in the body. You get a 202 and a job_id at once, and then you poll GET /api/v1/jobs/{job_id}. At 00:18:21 SGT we used async mode for page 2 of post B. The 202 already showed status succeeded.

At 00:18:30 the poll returned 15 rows. Polls are free and do not count toward the per-minute limit, as the everyinfra_get_job tool description says. Comments return in 2 to 3 seconds, so they do not need async mode. But if the same workflow also calls slow capabilities, async mode and polling keep agents from waiting on open requests.

Failures, retries and billing: what can make you pay twice

The table puts our observations next to workflow behavior. The "Workflow behavior" column comes from the workflows page. The "API billing" column comes from our raw responses.

EventWorkflow behaviorAPI billingDefense in the script
429 rate limitThe agent handles itNot billed ("Nothing was charged")Retry after retry_after, at most 3 times
422 bad value, such as sort=hottestThe agent handles itNot billedDo not retry; report the message
Agent stallsRestarts from the same prompt, up to 5 timesSuccessful calls run and bill againSet stallMs on fetch agents
Resume after a failed agentThat agent and all later agents run againRerun fetches bill againSplit fetch and label into two workflows
Same request within 6 minutesNot applicableBilled each time, and content changesDeduplicate by comment id
Label output fails the schemaRetries, fails after five attemptsNo API callKeep the schema small
Subscription usage limitPauses until reset, from v2.1.271, if conditions holdNo API callTest on a small slice first

For the usage-limit row, the session itself can also wait for a reset (interactive mode docs). The 422 test ran at 00:16:07 SGT, with sort set to hottest on purpose. The response said `"sort must be one of: default, most_liked, newest (got 'hottest')". It also gave the allowed list and did_you_mean: ["most_liked"]`, and it said that nothing was charged.

This kind of error suits a workflow. A fetch agent can read it, change one value and continue without a question to you. In our strict tool use article, we showed that strict tool definitions block bad enum values on the model side. In a workflow, a simpler fix is to hard-code the sort value in the fetch prompt.

The bill: how many calls and how much for 500 comments

The API has one unit price for this job. We read the catalog at 00:14 SGT on Nov 2, 2026. xiaohongshu/search, xiaohongshu/comments, xiaohongshu/sub_comments and xiaohongshu/comments_batch each cost $0.005556 per call (per post for the batch). Failed calls and empty results are free. For a paged capability, the call count is the row target divided by the rows per page, rounded up.

The three methods below count only comment pages. Search returns 20 posts per page. 500 comments usually need 20 to 40 posts, which adds 1 or 2 calls, $0.0056 to $0.0111.

Horizontal bar chart of the API cost to pull 500 Xiaohongshu comments by three methods. Catalog, 15 per page: 34 calls, $0.189. Measured, 12.5 per page: 40 calls, $0.222, in teal. 500 comments after noise removal: 54 calls, $0.300. xiaohongshu/comments costs $0.005556 per call.
MethodRows per pageCallsAPI cost
Catalog value1534$0.188904
Our measured mean12.540$0.22224
500 useful comments after 25% noise12.554 (pull 667 first)$0.300024
What this article spent: 10 billed requests, 12 billed unitsn/a10$0.066671

The next table lists every request in this test, in SGT on Nov 2, 2026. The 429, the 422 and the job poll were free.

Time (SGT)RequestHTTPRowsBilled
00:14:59xiaohongshu/search20020 posts$0.005556
00:15:22xiaohongshu/comments, post A page 120010$0.005556
00:15:33xiaohongshu/comments, post B page 120015$0.005556
00:15:49xiaohongshu/comments, post A page 220015$0.005556
00:15:59xiaohongshu/sub_comments, post B4290$0
00:16:07xiaohongshu/comments with sort=hottest4220$0
00:17:17xiaohongshu/sub_comments, post B2005$0.005556
00:17:38xiaohongshu/comments_batch, posts C, D, E20035$0.016667
00:17:55MCP everyinfra_call_api, post F20010$0.005556
00:18:21xiaohongshu/comments async, post B page 220215 at poll$0.005556
00:21:35xiaohongshu/comments, post B page 1 again20015$0.005556
00:21:55xiaohongshu/comments, post B page 1 again20015$0.005556

The third method needs a caution. The 25% noise share comes from our 6 posts, and almost all of it came from post A. With another keyword, the share can be 0 or much higher. So run the workflow on 3 to 5 posts first.

Read the filter count in the log() line, and then decide how many posts to pull. The docs give the same advice about cost. Run a small slice first. Watch each agent's tokens in /workflows, and stop the run if necessary (workflows page, "Cost").

We did not measure the Claude side, so we give only the parts that you can check. The Anthropic pricing page, read on Nov 2, 2026, lists these prices per million tokens. Haiku 5.5 is $0.10 input and $0.50 output for prompts up to 100,000 tokens. Sonnet 5.5 is $2 and $10. Opus 5.5 is $4 and $20.

Text-only, 500 comments are about 12,000 tokens by the page's rule of thumb (10 kB is about 2,500 tokens). Haiku input for that text costs less than $0.002. Most of the cost is elsewhere. Each agent carries a system prompt, tool definitions and its own reasoning, and that size depends on your version and your MCP tools. We did not measure it, so check /workflows for your numbers.

On a subscription, workflow usage counts toward your plan limits. The costs docs say that every subagent and every workflow agent sends its own requests. On the Reddit launch thread, one user said that one prompt used 700,000 tokens and 36% of a Max plan's usage. Another said that the first try used 1.2 million tokens (Reddit launch thread). These are user reports that we cannot verify. They still show why a small first test matters.

Context and tokens: why the fetch stage must slim the data

One comment returns about 1,445 bytes of JSON on average, and its text alone is about 95 bytes. That is about 15 times smaller. The raw JSON has the author name, avatar URL, IP region, tags, the first reply and a post URL with xsec_token, among 20 fields. Almost none of them help with labeling.

By the pricing page's rough ratio, one raw comment is about 361 tokens and its text is about 24 tokens. Chinese text uses a different ratio in practice. For exact numbers, use the token counting endpoint with your model.

Horizontal bar chart. One raw comment averages about 1,445 bytes of JSON, about 361 tokens. Comment text only averages about 95 bytes, about 24 tokens, in teal. That is about 15 times less. Tokens use the pricing page rule of thumb of 10 kB for about 2,500 tokens and are not measured.

This difference matters in two places. The first is the input to labeling agents. If fetch agents return raw JSON, each labeling agent reads 10,000 to 30,000 tokens of JSON per post. If they return text, it reads a few hundred tokens.

The second is prompt caching. The docs say that agents in one run with the same model, effort level, agent type, tools, output schema and working directory share a prefix. A later agent can read the earlier agent's cache. Workflow agents keep their cache for 5 minutes by default.

subagentPromptCacheTtl set to 1h keeps it for an hour, at a higher write price (workflows page, "Prompt caching in a fan-out"; prompt caching docs). In the skeleton, all fetch agents use the same haiku model, low effort and COMMENTS schema, so they can share that prefix. If you give each post a different model, you lose the shared cache.

The labeling stage: schema, label definitions and agreement

The labeling schema has two fields: id and label, with label limited to five values. Fewer fields mean fewer errors and fewer schema retries. If you also want budget, floor area and pets, add a separate extraction stage. Run it only on comments that the model labeled as a need. Do not add many optional fields to the labeling schema.

The Anthropic API constrains output with a json_schema in output_config.format (structured outputs docs). The workflow schema option does the same job inside Claude Code. The return value is already an object, so the script does not parse text.

Write each label definition so that a person can apply it. All four errors in our rule baseline came from vague definitions. A rhetorical question and a joke looked like needs. A comparison ("my budget model does not tangle hair") looked like a complaint. So the skeleton defines what each label means, not which words it contains. The examples are translated comments from our 6 posts.

LabelDefinitionExample from our data
needAsks which model to buy or asks for a link"140 square meters, tiles, lots of hair, budget 3,000, which one?"
complaintReports a product or service problem"The dock exhaust smells bad, how do I fix it?"
priceTalks about cost, cheaper copies or where to buy"3 yuan each, lasts a month"
praiseThe writer uses the product and recommends it"My budget Xiaomi does not tangle hair"
otherJokes, plot talk, off-topic"So the ghost hid in our house for four days...?"

What about a comment that is both a need and a price comment, such as "budget 3,000, which one?"? We label by what the writer wants, so this one is a need. If your team cares more about price bands, put price before need and state that order in the prompt.

The spot check borrows from the adversarial review that the workflows docs describe. Independent agents challenge each other's findings before the run reports them (workflows page, "When to use a workflow"). Labeling is not bug hunting, so our check agent does not try to refute the first labels. It labels the sample again without seeing them. Then the script computes the agreement rate. A different model (Sonnet in the skeleton) lowers the chance that both agents make the same mistake.

Agreement is only a lower bound. If both models call a joke a need, agreement is high but the label is wrong. So, for a new keyword, read 20 sampled comments yourself on the first run. Add the disagreements and the clear errors to the label definitions. Then relabel the whole batch.

Limits and cautions

The table lists seven limits and what to do about each. The first two rows come from the workflows page, "Behavior and limits".

LimitEffectWhat to do
The script cannot go onlineEvery API call happens inside an agent, which can make one extra callReturn calls and charged_usd through the schema and check the total
A run cannot ask you for inputNo review between fetch and labelOne workflow per stage
A run stops when you exitRunning agents start over on relaunchRelaunch from saved results
Comment URLs need xsec_tokenA "cleaned" URL failsKeep the URL exactly as returned
Only public commentsHidden or folded comments are missingExpect fewer negative views
Our labels are a rule baselineThe counts are not model outputRun the spot check on your own data
Versions differOld behavior in many tutorialsRun claude --version; this article uses 2.1.316

A run does not continue by itself after you background or exit the session. Claude must relaunch it from the saved results, and agents that were still running start over and spend tokens again (agent view docs).

The catalog asks for the full comment URL from a recent search result, with its query string. Page tokens last 24 hours, so a resume on the next day must start over. The 500-comment numbers come from the unit price and measured page sizes, and we did not pull 500 comments. This article uses the docs read on Nov 2, 2026.

Comment data includes IP regions and display names. Put only labels, counts and quotes without names in the report. Before you send or publish comment quotes, make sure that your use follows local law and the platform's terms. This article describes a data method and gives no legal advice.

FAQ

claude code workflows vs agent teams: which one fits comment sorting?

Agent teams are a lead agent with a few long-running peer sessions and a shared task list. They are experimental and need a flag (agent teams docs). A workflow is a script that starts dozens or hundreds of short agents. For hundreds of independent comments, a workflow fits better. For a few posts that you want to steer turn by turn, plain subagents fit better, as in our Claude Code subagents article. (Source of the question: the autocomplete suggestion "claude code workflows vs agent teams".)

claude code workflows vs skills: do I need both?

A skill is a set of instructions that Claude follows in the conversation. A workflow is a script that a runtime executes (workflows page). They work together. Put the label definitions in a skill, and let the workflow's agents load that skill. Then the definitions stay in one place. (Source of the question: the suggestion "claude code workflows vs skills".)

How many tokens does one Claude Code dynamic workflows run use?

The docs give no fixed number. They say that a run can use much more than the same work in a conversation. They suggest a small first slice and a look at /workflows (workflows page, "Cost"). Reddit users reported 700,000 to 1.2 million tokens for one prompt (Reddit). The API cost for this job is separate: about $0.222 for 500 comments. (Source of the question: the suggestion "claude code workflows cost", which has ten variants, such as "cost per token" and "cost reddit".)

What is ultracode, and should I turn it on?

Ultracode is a Claude Code switch. When it is on, Claude decides for each substantive task whether to write a workflow. It is not an effort level.

In a session, /effort ultracode (v2.1.284 or later) turns on the switch only and keeps the effort level. Only the launch flag claude --effort ultracode also sets effort to xhigh (model configuration docs). With it on, the "Large workflow" warning does not appear. For jobs that call a paid API, we suggest that you keep it off and ask for a workflow in one prompt. (Source of the question: the suggestions "ultracode claude" and "ultracode claude reddit".)

How do I make a workflow stop for a human review?

The docs answer that a run cannot ask for input, so a sign-off needs separate workflows (workflows page, "Behavior and limits"). You can also select a run in /workflows and press x to stop it, and completed agents keep their results. A Hacker News thread raised the same concern: the hard part is adding human judgment to long runs (Hacker News, 2026-05-28). (Source of the question: a commenter on the Reddit launch thread asked which signals stop subagents before they continue past a bad branch.)

How to start

  1. Get a key from the EveryInfra quickstart, then read the free catalog for xiaohongshu/search and xiaohongshu/comments.
  2. Add the remote MCP server to Claude Code with the command above. See remote MCP setup and the MCP server page.
  3. Ask Claude Code for a workflow that follows the skeleton on 5 posts. Read the plan, then select "Yes, run it".
  4. After the run, check tokens in /workflows and compare the API cost with the pricing page. Save a good run as a command with s.

The same skeleton works on other platforms if you change the capability names. See the Xiaohongshu data API, with comments, replies and batch comments. For other Chinese platforms, see the Douyin data API and the Weibo data API. To clean comments before labeling, read DeepSeek V4 in Claude Code for comment cleaning.

References

  1. Orchestrate subagents at scale with dynamic workflows. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/workflows
  2. Claude Code changelog. Anthropic. Latest entry 2.1.316, 2026-10-31. https://code.claude.com/docs/en/changelog
  3. CHANGELOG.md. anthropics/claude-code on GitHub. Read 2026-11-02. https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md
  4. Introducing dynamic workflows. Anthropic. 2026-05-28. https://claude.com/resources/articles/introducing-dynamic-workflows-in-claude-code
  5. Claude Code Adds Dynamic Workflows for Parallel Agent Coordination. InfoQ. June 2026. https://www.infoq.com/news/2026/06/dynamic-workflows-claude-code/
  6. Building agents: When to fold subagents into your main agent loop. Anthropic. 2026-10-22. https://claude.com/resources/articles/building-agents-when-to-fold-subagents-into-your-main-agent-loop
  7. Create custom subagents. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/sub-agents
  8. Run agents in parallel. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/agents
  9. Agent teams. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/agent-teams
  10. Skills. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/skills
  11. Settings reference. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/settings-reference
  12. Model configuration. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/model-config
  13. Environment variables. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/env-vars
  14. Agent SDK reference, TypeScript. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/agent-sdk/typescript
  15. Connect Claude Code to tools via MCP. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/mcp
  16. Manage costs. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/costs
  17. Configure permissions. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/permissions
  18. Permission modes. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/permission-modes
  19. Prompt caching in Claude Code. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/prompt-caching
  20. Run Claude Code programmatically. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/headless
  21. Hooks reference. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/hooks
  22. Sandboxing. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/sandboxing
  23. Plugin manifest reference. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/plugins/manifest-reference
  24. Agent view. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/agent-view
  25. Claude Code on the web. Anthropic. Read 2026-11-02. https://code.claude.com/docs/en/claude-code-on-the-web
  26. Remote Control. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/remote-control
  27. Interactive mode. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/interactive-mode
  28. Network configuration. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/network-config
  29. Scheduled tasks. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/scheduled-tasks
  30. Commands. Anthropic, Claude Code docs. Read 2026-11-02. https://code.claude.com/docs/en/commands
  31. Pricing. Anthropic, Claude platform docs. Read 2026-11-02. https://platform.claude.com/docs/en/about-claude/pricing
  32. Structured outputs. Anthropic, Claude platform docs. Read 2026-11-02. https://platform.claude.com/docs/en/build-with-claude/structured-outputs
  33. Rate limits. Anthropic, Claude platform docs. Read 2026-11-02. https://platform.claude.com/docs/en/api/rate-limits
  34. Token counting. Anthropic, Claude platform docs. Read 2026-11-02. https://platform.claude.com/docs/en/build-with-claude/token-counting
  35. Transports, specification 2025-11-25. Model Context Protocol. Read 2026-11-02. https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
  36. Tools, specification 2025-11-25. Model Context Protocol. Read 2026-11-02. https://modelcontextprotocol.io/specification/2025-11-25/server/tools
  37. Xiaohongshu capability catalog. EveryInfra. Read 2026-11-02 00:14 SGT. https://api.everyinfra.com/api/v1/social/catalog?platform=xiaohongshu
  38. Content review rules. Xiaohongshu Pugongying help center. Page dated 2026-10-22. https://pgy.xiaohongshu.com/help/detail?id=6495c527d1eedeeb48fb18b1f875650e&userType=4
  39. Pugongying home page. Xiaohongshu. Read 2026-11-02. https://pgy.xiaohongshu.com/
  40. ChatGPT Outperforms Crowd-Workers for Text-Annotation Tasks. Gilardi, Alizadeh, Kubli. arXiv 2303.15056, v2 2023-07-19. https://arxiv.org/abs/2303.15056
  41. Are Chatbots Reliable Text Annotators? Sometimes. Kristensen-McLachlan and others. arXiv 2311.05769, v2 2025-02-25. https://arxiv.org/abs/2311.05769
  42. Best Practices for Text Annotation with Large Language Models. Petter Törnberg. Sociologica, 2024. https://sociologica.unibo.it/article/view/19461
  43. Introducing dynamic workflows in Claude Code, discussion thread. r/ClaudeAI. May 2026. https://www.reddit.com/r/ClaudeAI/comments/1tq9ofy/introducing_dynamic_workflows_in_claude_code/
  44. Dynamic Workflows in Claude Code, discussion. Hacker News. 2026-05-28. https://news.ycombinator.com/item?id=48311705

Reference BL-64