# Comments API

Read the comments on your posts across nine social platforms, then reply, hide, like, delete or answer in private, with the same endpoints everywhere.

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

The Comments API reads the public comments on your posts and answers them, with the same endpoints on every connected account. Posts published outside Zernio work too. For DMs, see the [Messaging API](/products/messaging).

## What you can build

- **A comment moderation queue.** List the posts with new comments across a profile with `GET /v1/inbox/comments`, then read each thread with `GET /v1/inbox/comments/{postId}`.
- **Replies from your own tool or AI agent.** Post a reply under a comment with `POST /v1/inbox/comments/{postId}`.
- **Spam and abuse filters.** Hide a comment with `POST /v1/inbox/comments/{postId}/{commentId}/hide`, or delete it.
- **A private answer to a public question.** On Instagram and Facebook, DM the commenter with `POST /v1/inbox/comments/{postId}/{commentId}/private-reply`.
- **Real-time alerts.** React to every new comment with the [`comment.received`](/webhooks/inbox/comment-received) webhook.

## Platforms

<ProductPlatforms product="comments" />

Reading and replying work on every platform above. The moderation actions depend on what each network exposes:

| Platform | Like | Hide | Delete | Also |
|---|---|---|---|---|
| <PlatformName slug="instagram" page="inbox" /> | Limited release | Yes | Any comment on your posts | Private reply by DM |
| <PlatformName slug="facebook" page="inbox" /> | Yes | Yes | Any comment on your posts | Private reply by DM, photo replies |
| <PlatformName slug="twitter" page="inbox" /> | Yes | Replies in your own threads | Your own | No `comment.received` webhook |
| <PlatformName slug="threads" page="inbox" /> | No | Top-level replies | Your own | |
| <PlatformName slug="tiktok" page="inbox" /> | Yes | Yes | Your own | Pin, Business app connections only |
| <PlatformName slug="youtube" page="inbox" /> | No | No | Yes | Approve, reject or hold in moderation |
| <PlatformName slug="linkedin" page="inbox" /> | Yes | No | Any comment, on organization pages | Mention the comment's author |
| <PlatformName slug="reddit" page="inbox" /> | Upvote | No | Your own | Edit your own comments |
| <PlatformName slug="bluesky" page="inbox" /> | Yes | No | Your own | |

LinkedIn personal profiles can read and reply to comments on posts they published through Zernio.

With the Ads add-on, Meta ad comments appear in the same list. TikTok ad comments live under [ad comments](/platforms/tiktok-ads/ad-comments).

## How it works

Comments hang off posts. `GET /v1/inbox/comments` returns one row per post that has comments, newest first, across every account of a profile. Each row carries the post `id`, `platform`, `accountId` and `commentCount`.

`GET /v1/inbox/comments/{postId}?accountId=...` returns that post's comments, each with its `replies`, its author in `from`, and flags such as `canReply`, `canHide` and `isHidden`.

`postId` takes the Zernio post id or the platform's own post id, so posts published outside Zernio work too. Every write names the `accountId` that answers.

New comments arrive through [`comment.received`](/webhooks/inbox/comment-received). Instagram, Facebook, Threads and TikTok deliver it in real time. YouTube, LinkedIn, Bluesky and Reddit are polled, so it can take minutes. X never sends it.

## Your first request

You need an API key and a profile with connected accounts. This lists the posts that have comments across the profile.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: posts } = await zernio.comments.listInboxComments({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4', limit: 20 },
});

console.log(posts.data[0].id, posts.data[0].commentCount);
```
</Tab>
<Tab value="Python">
```python
from zernio import Zernio

client = Zernio()

posts = client.comments.list_inbox_comments(profile_id="66a1f0c2a4b9d3e8f1a2b3c4", limit=20)

print(posts["data"][0]["id"], posts["data"][0]["commentCount"])
```
</Tab>
<Tab value="curl">
```bash
curl "https://zernio.com/api/v1/inbox/comments?profileId=66a1f0c2a4b9d3e8f1a2b3c4&limit=20" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"
```
</Tab>
</Tabs>

Response (`200`):

```json
{
  "data": [
    {
      "id": "17912345678901234",
      "platform": "instagram",
      "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
      "accountUsername": "acme",
      "content": "Our fall collection is here",
      "createdTime": "2026-11-08T15:00:00Z",
      "commentCount": 42,
      "likeCount": 310
    }
  ],
  "pagination": { "hasMore": true, "nextCursor": "eyJjcmVhdGVkVGltZSI6..." },
  "meta": { "accountsQueried": 4, "accountsFailed": 0, "failedAccounts": [] }
}
```

Pass a row's `id` and `accountId` to [Get post comments](/comments/get-inbox-post-comments) to read the thread.

## Examples

### Reply under a comment

Send `accountId` and `message`. Add `commentId` to reply to one comment; without it, the reply is a new top-level comment on the post.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: reply } = await zernio.comments.replyToInboxPost({
  path: { postId: '17912345678901234' },
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    commentId: '17998765432109876',
    message: 'Thanks! The link is in our bio.',
  },
});
```
</Tab>
<Tab value="Python">
```python
reply = client.comments.reply_to_inbox_post(
    post_id="17912345678901234",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    comment_id="17998765432109876",
    message="Thanks! The link is in our bio.",
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/inbox/comments/17912345678901234" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "commentId": "17998765432109876",
    "message": "Thanks! The link is in our bio."
  }'
```
</Tab>
</Tabs>

```json
{ "success": true, "data": { "commentId": "17998765432109877", "isReply": true } }
```

Send an `Idempotency-Key` header to make a retry safe ([idempotency](/guides/idempotency)).

### Hide a spam comment

Hiding keeps the comment visible to its author and to you, and hides it from everyone else. It works on Instagram, Facebook, Threads, X and TikTok. Undo it with [Unhide comment](/comments/unhide-inbox-comment).

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
await zernio.comments.hideInboxComment({
  path: { postId: '17912345678901234', commentId: '17998765432109876' },
  body: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
});
```
</Tab>
<Tab value="Python">
```python
client.comments.hide_inbox_comment(
    post_id="17912345678901234",
    comment_id="17998765432109876",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/inbox/comments/17912345678901234/17998765432109876/hide" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "accountId": "66b2e19d8c3f5a7e9d0b1c2d" }'
```
</Tab>
</Tabs>

```json
{ "status": "success", "commentId": "17998765432109876", "hidden": true, "platform": "instagram" }
```

On YouTube, use [moderation](/comments/set-comment-moderation) instead: approve, reject or hold the comment.

### Answer a commenter in private

On Instagram and Facebook, a private reply sends the commenter a DM tied to their comment. Each comment takes one private reply, within 7 days.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: sent } = await zernio.comments.sendPrivateReplyToComment({
  path: { postId: '17912345678901234', commentId: '17998765432109876' },
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    message: 'Hi! Here is the price list you asked about: https://example.com/prices',
  },
});
```
</Tab>
<Tab value="Python">
```python
sent = client.comments.send_private_reply_to_comment(
    post_id="17912345678901234",
    comment_id="17998765432109876",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    message="Hi! Here is the price list you asked about: https://example.com/prices",
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/inbox/comments/17912345678901234/17998765432109876/private-reply" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "message": "Hi! Here is the price list you asked about: https://example.com/prices"
  }'
```
</Tab>
</Tabs>

```json
{ "status": "success", "messageId": "aWdfZAG1faXRlbToxOklHTWVzc2FnZAUlEOjE3ODQx", "commentId": "17998765432109876", "platform": "instagram" }
```

The DM then lives in the inbox as a conversation. To send it automatically when a comment matches a keyword, use [Comments to DM](/products/comments-to-dm).

## How it behaves

- **Lists are cached.** Both list endpoints can lag new comments by up to 10 minutes. Your own replies and deletes refresh that post's thread at once.
- **Follow the default sort to page.** `nextCursor` is reliable only with `sortBy=date` and `sortOrder=desc`.
- **Partial results.** An account that fails is named in `meta.failedAccounts`, and the other accounts still return.
- **Ads are included.** With the Ads add-on, Meta ad comments come back as rows with `isAd: true`. Filter them with `platform=metaads`.
- **Replies are free.** Comment replies never count toward [outbound messages](/pricing/inbox). Private replies are DMs, so they do.

## If it fails

| Error | Cause | Fix |
|---|---|---|
| `meta.accountsFailed` above 0 | An account's token stopped working | Reconnect the accounts in `meta.failedAccounts` |
| `400` `PLATFORM_LIMITATION` on TikTok | The account was connected through TikTok's developer app | Reconnect it, which uses the Business app |
| `400` `PLATFORM_LIMITATION` on a like or hide | The platform has no such action | Check the table above |
| Private reply `400` with `details.privateReplyConsumed: true` | The comment's one private reply is spent | Do not retry. Answer in the conversation instead |
| Second page empty or repeated | Paging with a non-default sort | Keep `sortBy=date` and `sortOrder=desc` |

## Related

- [List commented posts](/comments/list-inbox-comments)
- [Get post comments](/comments/get-inbox-post-comments)
- [Reply to a comment](/comments/reply-to-inbox-post)
- [Comments to DM](/products/comments-to-dm)
- [Inbox API](/products/inbox)
- [`comment.received` webhook](/webhooks/inbox/comment-received)