Ad comments
Read the comments on an ad, dark posts and TikTok ads included, with GET /v1/ads/{adId}/comments.
Read the comments on an ad's creative, including dark posts, with GET /v1/ads/{adId}/comments. It returns the same response shape as inbox comments, so the same rendering code works on both.
Read the comments
Dark posts are ad creatives that never went live organically on the Page feed. They are not in Zernio's post database, so the regular GET /v1/inbox/comments/{postId} cannot serve them.
On Meta, this endpoint uses stored story identifiers with a Marketing API fallback. TikTok listing uses the ad group.
The path segment takes any identifier Zernio indexes for the ad. No translation step is needed.
- the Zernio
_id(24 hex characters) - Meta's numeric
platformAdId(the value thecomment.receivedwebhook ships ascomment.ad.id) - the creative's
effective_object_story_idoreffective_instagram_media_id
import Zernio from '@zernio/node';
const zernio = new Zernio();
const { data: thread } = await zernio.adaccounts.getAdComments({
path: { adId: '66d4a1b2c3e4f5a6b7c8d9e2' },
query: { limit: 50 }
});
for (const comment of thread.comments) console.log(comment.from.name, comment.message);Response (200):
{
"status": "success",
"comments": [
{
"id": "1029384756102938_5566778899",
"message": "Does this ship to Spain?",
"from": { "id": "7788990011223344", "name": "Jamie Rivera" },
"created_time": "2027-02-14T10:31:05+0000"
}
],
"pagination": { "hasMore": false, "cursor": null },
"meta": {
"platform": "instagram",
"placement": "instagram",
"adId": "66d4a1b2c3e4f5a6b7c8d9e2",
"platformAdId": "120260000000000000",
"effectiveStoryId": "17912345678901234",
"instagramPermalink": "https://www.instagram.com/p/DGx7Yk2ScAb/",
"instagramAccountId": "66b2e19d8c3f5a7e9d0b1c2d"
}
}meta.effectiveStoryId is the underlying post the comments belong to. pagination.cursor pages the thread through the cursor parameter.
Reply through the regular inbox endpoint, POST /v1/inbox/comments/{postId}:
| Field | Value |
|---|---|
postId | meta.effectiveStoryId |
commentId | The comment's id |
accountId | The placement's own account |
That account comes back in the same response: meta.instagramAccountId on the Instagram side, meta.facebookAccountId on the Facebook side.
meta.facebookAccountId is null when no connected Page was used, and moderation is then impossible.
Any postId that is not a 24-character Zernio id goes to the platform as it is. A dark post replies fine without being in the post database.
TikTok ads
The same endpoint serves TikTok ads, in the same response shape, and TikTok additionally supports moderation.
Listing works from the stored ad group alone. There is no identity resolution or ad-detail lookup to find a video item ID.
meta.tiktokItemId can be null and does not prevent listing. That includes external ads TikTok no longer returns from ad details.
There is no ad-level identity or item-ID precondition to check before listing.
If the ad group is missing, Zernio fetches ad details to find it. Unavailable details return 404 ad_not_found, and a missing ad group returns 400 ad_not_commentable.
TikTok's comment API works on a date window rather than a cursor into all history. So two rules apply that Meta does not have:
- The window is at most 30 days. It defaults to the last 30 days when you pass neither
sincenoruntil. - Both accept a plain
YYYY-MM-DDdate.
Pagination stays a cursor. The cursor carries the window with it, so you do not re-send the dates while paging.
curl -H "Authorization: Bearer $ZERNIO_API_KEY" \
'https://zernio.com/api/v1/ads/1790166588666881/comments?since=2026-08-15&until=2026-09-09&limit=50'Each comment carries canReply, canDelete and canHide:
| Flag | Requires |
|---|---|
canReply | A first-level comment, comment-management permission, a video item ID and a supported identity |
canDelete | TikTok's own-comment deletion capability, a video item ID and a supported identity |
canHide | Only the advertiser and comment IDs, so it is true without an identity |
canReply and canDelete are false when the identity or item needed to moderate that comment is unknown.
These are per-comment capabilities, not a reason to block the listing. A direct reply or delete request can resolve missing identity or item fields lazily and succeed even after a false flag.
Empty pages. Listing filters each ad-group page to the requested ad, so a page can be empty while pagination.hasMore is true. Keep paging with the returned cursor and the same limit.
Reply to a comment, hide or restore it, or delete one of your own:
curl -X POST -H "Authorization: Bearer $ZERNIO_API_KEY" -H 'Content-Type: application/json' \
-d '{"text":"Thanks, sending you the specs now."}' \
'https://zernio.com/api/v1/ads/1790166588666881/comments/7412345678901234567/reply'
curl -X POST -H "Authorization: Bearer $ZERNIO_API_KEY" -H 'Content-Type: application/json' \
-d '{"hidden":true}' \
'https://zernio.com/api/v1/ads/1790166588666881/comments/7412345678901234567/hide'
curl -X DELETE -H "Authorization: Bearer $ZERNIO_API_KEY" \
'https://zernio.com/api/v1/ads/1790166588666881/comments/7412345678901234567'The moderation routes take the same since and until as the listing, because TikTok looks a comment up inside a window.
Pass the window you listed with when you act on a comment near the edge of it.
Organic TikTok comments have their own surface, the inbox comment endpoints, on accounts connected through TikTok's Business app.
This endpoint is the one that reaches comments on ad creatives, dark posts included.
Moderation is TikTok-only. On Meta these three routes answer 501.
Pick a placement
An ad that runs on the Facebook feed and the Instagram feed has 2 separate underlying posts with separate comment threads. They are the creative's effective_object_story_id and its effective_instagram_media_id.
placement picks one, taking facebook or instagram. With no placement, the Instagram side comes back when it exists and the Facebook side otherwise.
Instagram-placed comments are read through the token of the Instagram account that runs the ad. That account has to be connected to Zernio.
The identifiers themselves are read from the ad record, persisted during sync. Ads that predate the field use a Marketing API fallback.
If it fails
A 400 means the creative format exposes no commentable post:
{
"error": "This ad's creative has no commentable underlying post",
"type": "invalid_request_error",
"code": "ad_not_commentable"
}On Meta, Story ads and dynamic product ads return this, and so does a placement the ad does not run on.
A 422 with code: "ads_connection_required" means one of two things:
- The ads token is unavailable.
- No connected Instagram account on the profile can read the ad's media. Connect that account, or read the Facebook side with
placement=facebook.
A 403 with code: "feature_not_available" means the ad is on neither Meta nor TikTok.
Related
- Inbox comments: the same shape for organic posts.
- Creatives: where
effectiveObjectStoryIdcomes from. - Inbox webhooks:
comment.receivedfor ads as well as posts. - Get ad comments: every parameter.