Comments
List the comments on a post you published, reply to one, hide it, or delete it.
What can I do with comments?
For a post Chirpie published, you can list the comments it received, reply to one, hide one, and delete one, on the platforms that allow each of those. Comments are read from a stored snapshot that Chirpie refreshes on a schedule, so a listing is fast and costs you nothing beyond your plan's monthly sync allowance.
GET /api/v1/posts/:id/comments
POST /api/v1/posts/:id/comments/:comment_id/reply
POST /api/v1/posts/:id/comments/:comment_id/hide
DELETE /api/v1/posts/:id/comments/:comment_idEvery route takes Authorization: Bearer chirpie_sk_YOUR_KEY. The :id is Chirpie's own
post id, the one every Posts API response carries, and :comment_id is
the id of a comment from the listing.
A reply is a post. It is published to the platform like any other post, so it counts as one post against your monthly quota. Hiding and deleting cost nothing.
Which platforms support which actions?
| Platform | List | Reply | Hide | Delete |
|---|---|---|---|---|
| X/Twitter | Yes | Yes | No | Your own replies |
| Bluesky | Yes | Yes | No | Your own replies |
| Mastodon | Yes | Yes | No | Your own replies |
| LinkedIn (Page) (Coming Soon) | Yes | Yes | No | Your own comments |
| LinkedIn (profile) | No | No | No | No |
| Facebook (Coming Soon) | Yes | Yes | Yes | Any comment |
| Instagram (Coming Soon) | Yes | Yes | Yes | Any comment |
| Threads (Coming Soon) | Yes | Yes | Yes | Your own replies |
| Telegram | No | No | No | No |
Every listing carries the same table for the post you asked about, in
meta.capabilities, so you never have to hard-code it:
"capabilities": { "reply": true, "hide": true, "delete_own": true, "delete_any": true }delete_own and delete_any are separate because on most platforms you may only delete
replies you wrote. Offer a Delete control on a stranger's comment only when delete_any
is true.
An action the platform does not have returns 501 comment_action_unsupported with a
sentence naming what it does allow. It never returns success for something that did not
happen.
List Comments
GET /api/v1/posts/:id/comments
Authorization: Bearer chirpie_sk_YOUR_KEYQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Comments per page (max 100) |
cursor | string | None | The next_cursor from the previous page |
since | string (ISO 8601) | None | Only comments posted after this time |
sync | string | auto | auto refreshes when the platform's refresh window has passed, true asks for a refresh now (honoured a minute after the last one, 15 minutes on X), false never refreshes |
include_hidden | boolean | false | Include comments you have hidden. They carry hidden: true |
include_deleted | boolean | false | Include comments that are gone from the platform. They carry deleted: true and keep their text |
parent_id | string | None | Only the replies to this comment. Takes the comment's platform_comment_id, not its Chirpie id |
Response 200 OK
{
"data": [
{
"id": "6f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"post_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"platform": "facebook",
"platform_comment_id": "1234_5678",
"platform_parent_id": null,
"author": {
"handle": "@someone",
"display_name": "Someone",
"platform_id": "9876",
"avatar_url": null
},
"text": "Does this work with n8n?",
"created_at": "2026-09-18T10:02:11.000Z",
"likes": 3,
"replies": 1,
"hidden": false,
"deleted": false,
"own": false,
"replied": true,
"replied_at": "2026-09-18T10:09:40.000Z",
"reply_comment_id": "7a2d3e4f-5b6c-7d8e-9f0a-1b2c3d4e5f60"
}
],
"meta": {
"next_cursor": "MjAyNi0wOS0xOFQxMDowMjoxMVp8MTIzNF81Njc4",
"capabilities": { "reply": true, "hide": true, "delete_own": true, "delete_any": true },
"sync": {
"status": "ok",
"synced_at": "2026-09-18T10:10:00.000Z",
"reason": null,
"next_sync_after": "2026-09-18T10:11:00.000Z"
}
}
}replied is Chirpie's own record that you answered this comment, and reply_comment_id
points at the reply. It survives a handle change and a reply made outside Chirpie, which
is why it is a field rather than something you work out from the author. If the reply it
points at is deleted, replied goes back to false.
own is true for anything the connected account wrote, your replies and first comments
included, and their author is the connected account: its name, handle, id and avatar.
A comment with deleted: true keeps text exactly as Chirpie last stored it. Once the
platform has removed a comment, that is the only copy left, so it is returned rather than
blanked. hidden and deleted are independent: a comment can be both.
Pagination
Comments come back newest first. Page by passing the previous response's
meta.next_cursor back as cursor, and stop when next_cursor is null. The cursor is
opaque: do not parse it or build one yourself.
curl "https://chirpie.ai/api/v1/posts/POST_ID/comments?limit=50" \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY"
curl "https://chirpie.ai/api/v1/posts/POST_ID/comments?limit=50&cursor=MjAyNi0..." \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY"The sync object
A listing always answers from Chirpie's stored snapshot, and meta.sync says how fresh
that snapshot is and why.
status | Meaning |
|---|---|
ok | The snapshot is current |
unsupported | The platform exposes no comment read for this post |
permission | The connection needs re-authorizing. reason says what to do |
rate_limited | The platform is rate limiting. The snapshot is returned as it stands |
budget | Your plan's monthly comment allowance is spent |
plan | Your plan does not include comment syncing for this platform |
error | The last refresh failed. The snapshot is returned as it stands |
reason carries a short sentence for any status but ok, already worded for your users.
next_sync_after is the earliest moment a sync=true will be honoured.
How often does a listing refresh?
Two windows apply, both per post. An ordinary listing (sync=auto) refreshes once the
platform's refresh window has passed since the last sync. An explicit sync=true waits a
shorter window, because it is someone asking for the latest comments on purpose.
| Platform | sync=auto | sync=true |
|---|---|---|
| X/Twitter | 15 minutes | 15 minutes |
| Facebook, Instagram, Threads, LinkedIn (Page) | 5 minutes | 1 minute |
| Bluesky, Mastodon | 2 minutes | 1 minute |
X keeps its 15-minute window on sync=true too. Every X refresh spends your plan's X
comment read allowance, which is counted per reply X returns, so a shorter window there
would spend it faster without showing much more. A refresh that failed waits longer than
either window, doubling on each consecutive failure up to an hour, or as long as the
platform's own Retry-After when it names one; next_sync_after says when.
sync=true is a request, not a guarantee. A refresh that arrives inside the sync=true
window for that platform is skipped and the snapshot answers as it stands, with
sync.status: "ok" and next_sync_after naming the earliest moment another refresh will
be attempted. A refresh refused by your plan's allowance, by the platform, or by a
connection that needs re-authorizing answers from the snapshot with sync.status of
budget, plan, rate_limited or permission. Either way it is a 200, not an error:
the read succeeded.
The one exception is a sync=true on a post with no stored comments at all. There is no
snapshot to answer with, and an empty 200 would read as "this post has no comments", so
the refusal is returned instead: 402 comment_read_budget_exceeded, 429 comment_sync_rate_limited or 409 comment_permission_required, carrying the sentence
that would otherwise have been in sync.reason.
Reply to a Comment
POST /api/v1/posts/:id/comments/:comment_id/reply
Authorization: Bearer chirpie_sk_YOUR_KEY
Content-Type: application/jsonRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | The reply. Same per-platform character limits as a post |
curl -X POST https://chirpie.ai/api/v1/posts/POST_ID/comments/COMMENT_ID/reply \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "Yes, pass schedule_at and we will queue it." }'Response 201 Created
The reply, in the same shape as a listed comment, with own: true. The comment it answers
comes back from the next listing with replied: true.
A reply counts as one post against your monthly quota, and an X reply whose text contains a link carries the same X link-post charge a normal X post does.
Hide a Comment
POST /api/v1/posts/:id/comments/:comment_id/hide
Authorization: Bearer chirpie_sk_YOUR_KEY
Content-Type: application/json| Field | Type | Required | Description |
|---|---|---|---|
hidden | boolean | Yes | true hides the comment, false shows it again |
Response 200 OK
The whole comment comes back, with hidden set to what you asked for, so you can render
the updated row without listing again.
{ "data": { "id": "6f1c2d3e-...", "hidden": true, "...": "every other comment field" } }A hidden comment stays in Chirpie's listing when you pass include_hidden=true, so your
own moderation history is never lost even though the platform stops returning it.
Only Facebook, Instagram and Threads have a hide. Everywhere else this returns
501 comment_action_unsupported.
Delete a Comment
DELETE /api/v1/posts/:id/comments/:comment_id
Authorization: Bearer chirpie_sk_YOUR_KEYResponse 200 OK
{ "data": { "id": "6f1c2d3e-...", "deleted": true } }On Facebook and Instagram you can delete any comment on a post you published. Everywhere
else you can delete only replies you wrote, and deleting someone else's returns
501 comment_action_unsupported. Check meta.capabilities.delete_any before offering the
control.
The comment is kept in Chirpie with deleted: true and its text, and comes back with
include_deleted=true, so a deletion is recorded rather than erased. Deleting the same
comment again answers the same 200, so a retry is safe.
What happens to the replies
Some platforms delete a comment's replies along with it. Chirpie records those as deleted too, so the thread matches what is on the platform:
| Platform | Replies under a deleted comment |
|---|---|
| Deleted with it | |
| Deleted with it | |
| Threads | Deleted with it |
| LinkedIn (Page) | Deleted with it |
| X/Twitter | Stay live |
| Bluesky | Stay live |
| Mastodon | Stay live |
The same applies when a comment is deleted on the platform itself: the next sync records the comment and, on the first four platforms, the replies under it as deleted.
How many comment syncs do I get?
One sync is one refresh of one post, whatever it costs upstream. Listings from the snapshot are unlimited.
| Plan | Comment syncs/mo | X comment reads/mo |
|---|---|---|
| Free | 200 | Not included |
| Agent | 1,000 | 500 |
| Starter | 5,000 | 2,000 |
| Pro | 25,000 | 10,000 |
| Scale | Custom | Custom |
| Enterprise | Unlimited | Unlimited |
X is metered a second way because X bills per object returned: an X comment read unit is one reply X handed back. X accounts connected with your own X API credentials are exempt from the X read allowance, and you pay X directly.
On the Free plan, a listing for an X post answers from the snapshot with
sync.status: "plan". It is a 200 with an explanation, not an error. See
Rate Limits for the full table.
Error Responses
| Status | Code | Condition |
|---|---|---|
400 | bad_request | A missing or malformed field, or an unrecognised query value |
400 | validation_error | A reply with no text, or text over the target platform's character limit |
402 | comment_read_budget_exceeded | Your plan's monthly comment allowance is spent and there is no snapshot to answer with |
402 | x_link_posts_require_paid_plan | An X reply containing a link, from a Free-plan account |
404 | not_found | The post or the comment does not exist, or belongs to another user |
409 | comment_permission_required | The connection needs re-authorizing, and on a listing only when you asked for sync=true and there is no snapshot to answer with. Reconnect the account from the dashboard |
429 | comment_sync_rate_limited | The platform is rate limiting and there is no snapshot to answer with. Retry after Retry-After |
429 | usage_limit_exceeded | Your monthly post quota is spent, and a reply is published as a post |
429 | rate_limited | The burst limit on your API key |
501 | comments_unsupported | The platform exposes no comment read for this post |
501 | comment_action_unsupported | The platform allows reading but not this action |
501 | platform_coming_soon | The platform is recognised but not available yet |
502 | upstream_error | The platform refused the reply, hide or delete. Nothing changed |
See Error Codes for the full reference.