Build in Public · LF-02
Batch fetching Xiaohongshu comments: from note lists to verifiable results
Step-by-step integration of Xiaohongshu comments_batch: preparing complete note URLs, distinguishing URL count from comment count, identifying partial and missed results, deduplicating by note, and handling empty results, retries, and target billing.
When conducting Xiaohongshu comment research, the first step is often organizing note URLs rather than analyzing sentiment. Multiple contributors submit lists containing duplicate notes; over time, some links expire; a batch request returns 200, yet the results only show a subset of notes. Without resolving these issues first, subsequent "high-frequency issues" and "negative ratios" may simply be another expression of collection gaps.
The xiaohongshu.comments_batch endpoint, referenced as EveryInfra, accepts an array of note URLs to submit multiple targets at once. This guide starts with a single note and details how to scale up to a logged, verifiable batch process: preparing targets, confirming parameters, sending requests, organizing comments by note ownership, and determining which targets can be retried.
Input constraints and response handling in this guide are verified against the public catalog and source code for 2026-09-04. Business requests are adaptable templates, and offline examples are clearly marked synthetic data that do not impersonate real comments for this round. Before calling, confirm that your targets, intended use, and retention scope are approved.
Selecting the right capability: searching notes, comments, and replies are distinct tasks
The search endpoint discovers notes using keywords, note reads a specific note's details, comments targets a single note, and comments_batch targets multiple notes. When subordinate replies for a specific comment are needed, the catalog provides sub_comments, which also requires comment_id as input. These interfaces cannot be interchanged simply by changing a parameter name.
Determine your research object first. For example, analyzing user feedback in a specific set of product notes requires a note list, while obtaining all comments mentioning a brand involves note discovery scope, retrieval omissions, comment coverage, and time windows, and cannot promise complete coverage based solely on batch comment capabilities.
This must also be distinguished from official account integration. The official Xiaohongshu API reference provides endpoints for authorization, tokens, and user information; if your requirement is user login or reading authorized account profiles, start from the corresponding official workflow. The request contract for EveryInfra in this article is not that official account contract, nor do official links prove that comment reading has platform endorsement.
Step 1: Verify inputs, modes, and fields in the catalog
curl -fsS --max-time 30 \
'https://api.everyinfra.com/api/v1/social/catalog?platform=xiaohongshu' \
| jq -e '.capabilities[] | select(.action == "comments_batch") | {
action, required_params, optional_params, param_meanings,
mode, available, default_limit, max_limit, response_fields
}'In this catalog version, the required parameter is urls, the mode is sync, the default count is 30, and max_limit is 200; target arrays are billed per target. Note that there are two quantities here: the length of urls is the number of notes, and the count in the result is the number of delivered records. Treating 200 as "submitting 200 notes at once" leads to errors in the first step of request design.
The current source code has explicit input checks for comments_batch: urls must be a non-empty list with a maximum of 20 items, each item must successfully resolve to a note identifier, and each must include a non-empty xsec_token parameter. Exceeding the target limit or missing necessary link information triggers a parameter error path. This 20 comes from this implementation check, not from calculating max_limit.
The presence of a token in a link does not mean the server has verified it is still fresh. Current parameter checks can determine whether identifiers and parameters exist, but cannot determine whether a target is deliverable at this moment based solely on strings. Do not write a local checker that assumes any link containing a token is valid and treat its pass rate as the comment collection success rate.
Step 2: Save request links and note identities separately
Use complete note URLs obtained through approved channels when making requests. Do not strip query parameters before submission to make URLs cleaner, nor guess or forge access parameters or mix parameters from one target onto another. Short links and share text must first be organized into note links accepted by this capability; do not assume the interface will parse arbitrary share text for you.
Meanwhile, deduplication should not rely solely on comparing complete URL strings. Links to the same note may carry different query parameters, and string-based deduplication leaves multiple identical targets. Separate the identity key from the currently usable link: the identity key consists of the platform and note ID, while the link retains its original value along with the retrieval timestamp.
- Target list: note identity key, complete request link, source channel, retrieval timestamp, and why this research requires it.
- Deduplication list: which inputs point to the same note, and which confirmed link is finally adopted; preserve source relationships rather than simply deleting duplicates.
- Checklist for review: unparseable links, links missing parameters, and links with conflicting sources for the same identity. Handle these items first rather than mixing them into paid batches.
Keep complete links only in controlled inputs or task storage. Logs, tickets, and exported reports typically only require note IDs and processed source references, and time-sensitive access parameters should not be copied everywhere. When sharing readable sources, maintain separate links suitable for public display without breaking actual request inputs.
Step 3: Verify requests and attribution with a single note first
The examples below use the batch action format but place only a single approved note in the array to easily verify whether comments actually belong to that target. Assume the server has configured EVERYINFRA_API_KEY and EVERYINFRA_XHS_NOTE_URL; the latter should be an actual complete note link, not a placeholder created for this article.
: "${EVERYINFRA_API_KEY:?set the API Key first}"
: "${EVERYINFRA_XHS_NOTE_URL:?set the authorized full note URL first}"
jq -cn --arg url "${EVERYINFRA_XHS_NOTE_URL}" '{
platform: "xiaohongshu",
action: "comments_batch",
params: {urls: [$url], limit: 5}
}' | curl --silent --show-error --include --max-time 180 \
'https://api.everyinfra.com/api/v1/social' \
-H "Authorization: Bearer ${EVERYINFRA_API_KEY}" \
-H 'Content-Type: application/json' \
--data-binary @-This sample serializes URLs via jq rather than manually concatenating JSON; limit: 5 is used to check a small number of records, and a client timeout of 180 seconds is also an example setting. It does not prove five records will be returned, let alone that the note has only five comments. The initial check should confirm the response envelope, comment IDs, note_id or note_url, null values, and time formats before considering additional targets.
Step 4: When chunking batches, do not treat limit as a delivery promise per note
After single-target verification, deduplicated links can be divided into small batches below the current constraints. Start with smaller batches and record the actual targets submitted in each batch rather than immediately treating the 20 item limit as a fixed batch size. The upper limit indicates where the request is allowed to go, not whether your wait budget, data volume, or research needs are suited to running the maximum at once.
An implementation detail must be made clear here: current code applies limit to collection settings per target and subsequently truncates the final merged list based on this value. Therefore, passing limit: 5 to multiple targets does not mean "five items will be delivered per note." Even if a target is not in missed, the final returned truncated list may not retain its records.
If every note requires independent acceptance, initial versions can organize tasks by single-target requests and verify returned results separately; do not treat "batching" as requiring all notes to share a single response. When using multi-target batches, verify result attribution per target, incorporating quantity semantics into integration acceptance rather than hardcoding "note count × limit" as the expected receivable count.
Batch identities should also be independent of API requests. Your own batch_id represents a set of business targets, while an ID returned by an API call represents a single invocation. Retries generate new attempt records that link back to the original batch, avoiding overwriting the initial response and losing tracking threads for issues.
Step 5: Interpret partial, missed, and results per target
The synchronous response for this capability places the comment list in results, with count indicating the number of returned records. When some targets are not delivered, the response includes partial: true and missed; missed marks undelivered targets and is not a list confirming "these notes have zero comments."
When organizing results, first map each comment back to the target list using note_id or a confirmed note_url. Unmappable records enter the review area and cannot be assigned to the first note just to fill out reports. Absence from missed does not equate to acquiring all comments for that note; boundaries such as quantity truncation, missing fields, and unreached history still apply.
Below is an offline JavaScript synthetic example demonstrating target-level status classification only; note-a and similar values are fictitious identities, not valid platform IDs, nor API responses. Inputs are already converted to note identities on the application side, and real usage requires completing URL and note_id mapping first.
function classifyBatch(targetIds, rows, missedIds) {
const targets = new Set(targetIds);
if (targets.size !== targetIds.length) throw new Error("duplicate target");
const missed = new Set(missedIds);
const counts = new Map(targetIds.map(id => [id, 0]));
for (const id of missed) {
if (!targets.has(id)) throw new Error("unknown missed target");
}
for (const row of rows) {
if (!targets.has(row.note_id)) throw new Error("unmatched row");
counts.set(row.note_id, counts.get(row.note_id) + 1);
}
return targetIds.map(note_id => {
const count = counts.get(note_id);
if (count > 0 && missed.has(note_id)) {
throw new Error("conflicting target state");
}
return {
note_id, count,
state: count > 0 ? "records_observed"
: missed.has(note_id) ? "not_delivered" : "unconfirmed"
};
});
}
console.log(classifyBatch(
["note-a", "note-b", "note-c"],
[{note_id: "note-a", id: "synthetic-comment"}],
["note-b"]
));The third target has no records and no explicit undelivered flag, so the example leaves it as unconfirmed rather than filling it in as "zero comments." records_observed similarly indicates only that records were seen, not all_comments_complete. Keeping status names aligned with this distinction ensures subsequent statistics do not inadvertently expand conclusions.
Step 6: Handle comment deduplication and content updates in two layers
The current catalog lists id, text, like_count, posted_at, ip_location, author_name, author_id, sub_comment_count, note_id, note_url, and platform. These are field declarations and do not guarantee values for every record. Check actual returned shapes before saving, especially comment IDs and parent notes.
curl -fsS --max-time 30 \
'https://api.everyinfra.com/api/v1/social/fields?platform=xiaohongshu&action=comments_batch' \
| jq '{fields, undocumented}'When stable IDs are available, use "platform + note ID + comment ID" as a business deduplication key, updating observation times and mutable fields when the same comment is fetched again. Do not use nicknames as keys: nicknames can change and are not unique identifiers. Nor should records be deleted based solely on identical text; different users may genuinely write the same thing.
When stable IDs are missing, combinations such as "author identifier + timestamp + text summary" can be retained for approximate deduplication, but must be marked as low confidence rather than reliable identities. Engagement metric changes and text edits should also be separated from new comments: a new observation of the same comment is not a new comment.
sub_comment_count provides reply count information and cannot be used alone to claim retrieval of all nested reply bodies. When subordinate replies are needed, verify against the sub_comments catalog and actual returns. When performing time analysis, separate posted_at from your recorded observed_at; parsing failures must not be silently replaced with the request date.
If implementing this identity rule in PostgreSQL, composite unique constraints can express combined identities, but missing IDs must be handled separately. Official documentation notes that NULL values under default unique constraints do not compare as normally equal; adding unique does not automatically resolve duplicate records with incomplete identities. This is a local storage design example and does not involve the internal database of the API.
Step 7: Audit costs per target and retry only clearly defined targets
Such batches are measured by submitted targets, not billed by HTTP calls or returned comment counts. Deduplicate before submission and retain billing information after return. The current implementation includes corresponding refund fields for partially undelivered targets, including total_targets, billed_targets, and refunded_credits when applicable; do not assume all bills contain these three fields, nor use count to replace billed_targets.
Two counters in particular must not be conflated: a note returning multiple comments is still a single target, and a note returning fewer than expected comments cannot be billed a refund proportionally by count. Use responses and billing records as the basis for audits, and mark cases where fields are missing as unconfirmable from the response. This guide provides no fixed unit prices; actual pricing and account conditions are subject to terms at the time of call.
Determine the failure category before retrying. 422 typically requires modifying inputs, such as correcting links or narrowing the target array; clearly undelivered targets should only enter limited retries when causes are checked, use remains approved, and retries are meaningful. Do not mix targets that already returned records back into failed batches.
Network timeouts differ from missed: a timeout means you did not receive a complete response and cannot confirm which targets in the batch executed. In such cases, retain unknown attempts and query call logs or request verification rather than automatically resending the entire batch. Client batch_id does not provide server-side idempotency guarantees; retry limits for non-idempotent requests can be referenced in official HTTP specifications.
Build research datasets after obtaining comments
Analysis tables should ideally trace back to specific notes and comments, but do not need to retain all available fields. When researching product issues, comment text, source references, timestamps, and topic tags may suffice; whether nicknames, author identifiers, and ip_location are stored should be determined by practical purposes rather than keeping everything long-term simply because it was returned.
Place model judgments in a separate layer and retain model versions, classification rule versions, and manual corrections. For samples involving irony, negation, or quoting others, labels may be unstable; generate reports displaying verifiable examples and sampling scopes rather than packaging model scores as true user attitudes.
Specifically, do not use successfully returned partial comments as the denominator for an unknown total. An explainable report clarifies: how many deduplicated targets were checked, which had records, which were undelivered or unconfirmed, and which observation window was covered. It answers questions within this specific sample rather than unproven whole-platform reputation.
Define scope and retention rules before scaling up
Public visibility does not automatically resolve content rights, personal information processing, and redistribution permissions. You must review applicable platform rules and specific use cases to determine who can access original text, how long it is retained, when it is deleted, and whether results may be processed by other systems. This guide does not provide methods to bypass logins, privacy settings, or access restrictions.
Before formal batch runs, check at least one deliverable target, one input error, one partial result, and one unknown state recovery; the business meaning of empty results should be verified separately. If these situations cannot yet be explained, keep things small-scale without expanding request volumes to mask process gaps.
Retention scope should also extend to diagnostic logs. OWASP recommends removing or handling access tokens, session identifiers, and sensitive personal data; troubleshooting should prioritize leaving request references, target counts, and status summaries rather than treating complete comment batches as default log fields.
What a reliable batch should leave behind upon completion
The final output should include more than just a comment file; it should also contain deduplicated target lists, request identifiers for each attempt, target-level collection status, comment identities and sources, actual billing records, and items still requiring review. This ensures the next run knows precisely which targets to process next without re-scraping from scratch.
Walk through the pipeline with a single note first before expanding to small batches. Batch interfaces reduce repetitive request assembly work; what makes comment research truly credible is your ability to explain the provenance of every record and honestly account for what was missing.