Skip to main content
Fetch Many Comments
Returns one level of a comment thread, cursor-paginated:
  • entityId → the entity’s top-level comments.
  • parentId → that comment’s direct replies.
One of the two is required. You can send both: parentId decides the level, and entityId must match the parent’s entity (otherwise the page is empty). Each request can also bring a few levels of replies nested inside every returned comment (replyDepth, replyLimit), each with its own “load more” cursor — so a threaded view can render its first screen with a single request. To list a user’s comments across entities, use Fetch Comments by User instead. Authentication is optional. When the caller is signed in, comments by users they are block-edged with are marked or shown as placeholders (see Blocked authors).
For previews (for example, the best comment under an entity in a feed), use include=topComments on the entity fetch instead — it returns real content only. Thread fetches include placeholders.

Query Parameters

Thread

string
Return this entity’s top-level comments. Required unless parentId is sent.
string
Return the direct replies of this comment. Required unless entityId is sent.

Pagination

number
default:"10"
How many comments to return at the level being fetched (top-level comments for entityId, direct replies for parentId). A whole number of at least 1 — 0, a negative number or a non-integer value returns 400 comment/invalid-query. Values above 100 are treated as 100.
string
Continue after the last comment of a previous response. Leave it out on the first request (an empty cursor is treated the same way), then pass pagination.nextCursor from the previous response. The cursor must be used with the same sortBy, sortDir and sortByReaction it was created under. See Cursors.

Sorting

string
default:"createdAt"
Sort for the level being fetched. One of:
  • createdAt — by creation time.
  • top — by the raw count of one reaction (sortByReaction, default upvote). Nothing is subtracted — a comment with 10 upvotes and 8 downvotes ranks above one with 5 upvotes.
  • controversial — many up/down votes with a close split. Comments with no up/down votes sort last (under desc).
Ties are broken by creation time, then by ID, in the same direction — so ties are newest-first under desc and oldest-first under asc.
string
default:"desc"
asc or desc. Applies to every sort, including top and controversial.
string
default:"upvote"
With sortBy=top only: the reaction to rank by. One of upvote, downvote, like, love, wow, sad, angry, funny. Sending it with any other sortBy returns 400.
new and old are deprecated sortBy aliases: new is createdAt with the requested sortDir, old is createdAt ascending. They still work, but responses carry a non-blocking Deprecation header — use createdAt with sortDir instead. They aren’t accepted for replySortBy.

Nested replies

number
default:"0"
How many levels of replies to nest under each returned comment, from 0 (none) to 5.
number
default:"3"
How many replies per comment in the nested levels only. Must be at least 1 — to nest nothing, use replyDepth=0. Values above 100 are treated as 100.
string
Sort for the nested levels: createdAt, top or controversial. Defaults to the level’s sortBy.
string
asc or desc for the nested levels. Defaults to the level’s sortDir.
string
The reaction the nested levels rank by. Only accepted when the nested sort is top (replySortBy=top, or sortBy=top without replySortBy) — otherwise 400. Defaults to sortByReaction when the level is also sorted by top, else upvote. When replySortBy is omitted, all three reply-sort params follow the level’s sort.
The params come in pairs: limit and sortBy/sortDir/sortByReaction describe the level you’re asking for; replyLimit and replySortBy/replySortDir/replySortByReaction describe what’s nested underneath. A parentId request is a request for replies, so to get replies oldest-first there, send sortBy=createdAt&sortDir=asc.

Request size limit

Before running any query, the server computes the most comments a request could return:
using the values after limit and replyLimit are capped at 100. Requests above 500 are rejected with 400 comment/reply-budget-exceeded — they are never silently trimmed. For example, limit=10&replyDepth=2&replyLimit=3 can return up to 10 × 13 = 130 comments, while limit=10&replyDepth=5&replyLimit=2 (10 × 63 = 630) is rejected.

Optional associations

string
Comma-separated list of associations to include, applied at every level (nested replies included):Every comment in a thread belongs to the entity you asked for, so entity, space and parent aren’t available here (see Unsupported parameters).

Space-scoped reputation

This endpoint has a space in context, so it accepts the opt-in reputation params. They add a spaceReputation field to each populated author user (at every level), alongside the always-present reputation total. Requires the reputation bundle. See the Reputation data model for the full contract.

Response

Comment[]
The comments at the requested level, in sort order. Each item is a Comment.
string | null
Pass this as cursor to get the next batch. null when there’s nothing more.
boolean
Whether more comments exist after this batch. Exact — use it (not repliesCount) to decide whether to show a “load more” button.

Nested replies

With replyDepth above 0, each comment carries a replies field with the same envelope as the top level: { data, pagination }. A nested replies.pagination.nextCursor continues that comment’s replies — see Cursors.
  • Comments at the last nested level have no replies field — their replies weren’t fetched. replies: { data: [] } means they were fetched and there are none.
  • With replyDepth=0 (the default), no comment carries replies.
  • Placeholders and flagged comments get nested replies like any other comment, so other people’s replies under them stay in place.

Counts

repliesCount is the number of direct replies a comment has, for display. It counts every reply the thread contains — including deleted, removed and unavailable ones, which appear as placeholders — so it’s the same number for every viewer and always matches what paging through the replies returns. Drive buttons from hasMore.

Deleted and removed comments

Deleted and moderator-removed comments stay in the thread as placeholders, so the replies under them aren’t lost. A placeholder keeps its position, id, parentId, timestamps, reactionCounts and repliesCount, with the author and content blanked. A moderator-removed comment is shown in full to its own author. See Placeholders.

Blocked authors

Every comment carries two flags, isAuthorBlocked and isUnavailable. Both are always present, and always false when the caller isn’t signed in or the moderation bundle isn’t installed. Otherwise: isUnavailable is deliberately neutral: it means the comment exists but can’t be shown to this caller, without saying why. Replying to or reacting to an isUnavailable comment is refused, so hide those controls. When the entity’s author has blocked the caller, the whole thread is unavailable to them: a request with that entityId, or with the parentId of any comment on that entity, returns an empty page — exactly what an entity that doesn’t exist returns.

Cursors

A cursor is an opaque string. It records the sort it was created under and the last comment you received, and the next request continues right after that comment. Comments posted in the meantime don’t shift what you get.
  • Send the same sortBy, sortDir and sortByReaction with a cursor as the request that returned it. A different sort returns 400 comment/cursor-sort-mismatch; to change the sort, start over without a cursor.
  • A cursor doesn’t record which entity or comment it came from. Sending it with a different entityId or parentId just continues from the same sort position there.
  • Continuing nested replies: a replies.pagination.nextCursor was created under the reply sort. Continue it with a parentId request for that comment, sending the reply sort as sortBy / sortDir / sortByReaction:
    The SDK’s useReplies does this for you.
  • Vote-ranked sorts can shift: under top and controversial, a comment whose votes change between two requests can move past the cursor — it may be skipped (it shows up on refresh) or appear twice (de-duplicate by id). createdAt is exact.

Unsupported parameters

These parameters return 400 with code comment/unsupported-param, a param field naming the parameter, and an error message pointing to the replacement: Other unknown query parameters are ignored.

Error Responses

Checks run in this order; the first failing check is returned.
Returned on a project without the comments bundle, ahead of the checks below. The body has no error field: it carries the code, the missing tables, and a link to the project’s Database page in the dashboard, where the bundle is installed. See Bundles.
Returned with code comment/invalid-query for invalid values — for example an unknown include value, sortByReaction without sortBy=top, replySortByReaction when the nested sort isn’t top, limit below 1 or not a whole number, replyDepth above 5, or replyLimit below 1.

See Also