Comments and metrics on a challenge
Refresh and read the comments and post metrics stored for a challenge post, as engagement verification uses them.
A challenge keeps its own copy of the post's comments and metrics. A refresh route reads the platform and stores what it finds; the GET routes return the stored copy and never touch the platform. Nothing refreshes on a schedule.
For a one-off read of any public post, without a campaign, use the Comments and Post metrics lookups instead.
List stored comments
/admin/challenges/{challengeId}/comments?page=1&limit=20curl "{API_BASE_URL}/admin/challenges/1/comments?page=1&limit=50" \
-H "Authorization: Bearer <API_TOKEN>"{
"data": [
{
"id": 1,
"platformCommentId": "17890012345678",
"username": "creator_jane",
"commentText": "Love this product! #SummerVibes",
"commentedAt": "2026-06-02T14:21:07.000Z",
"createdAt": "2026-06-02T15:00:11.000Z"
}
],
"pagination": { "page": 1, "limit": 50, "total": 347 }
}| Field | Type | Meaning |
|---|---|---|
id | integer | Stored row id |
platformCommentId | string | The platform's comment id. Used for de-duplication |
username | string | Commenter's handle |
commentText | string | |
commentedAt | date-time | When the comment was posted |
createdAt | date-time | When the row was stored |
Refresh comments
Reads the post and stores what it finds, newest first, and for Instagram threaded replies on a best-effort basis. New comments are inserted; existing ones are matched on platformCommentId and left unchanged.
One call does not always reach the end of a large post. The call walks the newest comments, then works backwards through the history from where the last call stopped, and gives up after about four minutes. historyComplete tells you whether there is more to come; call it again to carry on.
curl -X POST {API_BASE_URL}/admin/challenges/1/comments/refresh \
-H "Authorization: Bearer <API_TOKEN>"{ "data": { "totalFetched": 347, "sweepStatus": "swept", "historyComplete": true } }| Field | Meaning |
|---|---|
totalFetched | Comments now stored for the post, not the number this call added |
sweepStatus | swept when this call walked the comments. in_progress when another read of the same post was already running, so this call left the walk to it; on Instagram the reply pass still runs either way |
historyComplete | true when the stored comments are as complete as they will get. false when there is older history still to fetch; call again to continue |
Once 6,000 comments are stored for a post, the walk back through older history stops and historyComplete reports true. Newer comments carry on being stored, so the total keeps growing past 6,000.
Worth calling once when you create a campaign on a post that already has comments. While an attempt is being verified, the API reads new comments itself.
Comment refresh errors
When the post cannot be read, the refresh route returns one of the data lookup errors. 422 and 404 are permanent for that post; 429, 502, and 503 are worth retrying.
Latest metrics snapshot
/admin/challenges/{challengeId}/metricscurl {API_BASE_URL}/admin/challenges/1/metrics \
-H "Authorization: Bearer <API_TOKEN>"{
"data": {
"likeCount": 5432,
"commentCount": 312,
"shareCount": 89,
"viewCount": 125000,
"thumbnailUrl": "https://...",
"fetchedAt": "2026-06-02T15:00:11.000Z"
},
"lastRefresh": {
"status": "success",
"errorCode": null,
"attemptedAt": "2026-06-02T15:00:11.000Z",
"durationMs": 2713
}
}data is the last successful snapshot, or null when none has been taken yet. On its own, fetchedAt cannot tell you whether the numbers are current: a challenge whose refreshes keep failing goes on returning its last good snapshot. lastRefresh records the most recent refresh and how it ended. If lastRefresh.attemptedAt is much newer than data.fetchedAt, the numbers are stale and refreshes are failing.
| Field | Type | Notes |
|---|---|---|
likeCount | integer or null | null when the author hides the like count |
commentCount | integer | |
shareCount | integer or null | Always null on Instagram |
viewCount | integer or null | Reels and TikTok videos; null for image posts |
thumbnailUrl | string or null | The post's cover image. A signed URL that expires within days; refresh to get a working one |
fetchedAt | date-time | When this snapshot was taken |
lastRefresh.status | string | success, rate_limited or error |
lastRefresh.errorCode | string or null | The data lookup error when the refresh failed |
lastRefresh.attemptedAt | date-time | When the most recent refresh ran. lastRefresh is null if the challenge has never been refreshed |
lastRefresh.durationMs | integer | How long that refresh took |
Refresh metrics
curl -X POST {API_BASE_URL}/admin/challenges/1/metrics/refresh \
-H "Authorization: Bearer <API_TOKEN>"| Status | Meaning |
|---|---|
200 | The snapshot was taken. data is the new PostMetrics object |
202 | Reads are being throttled upstream, so the refresh was queued and runs in the background. Read the latest snapshot again after retryAfterSeconds |
429 RATE_LIMITED | This challenge was refreshed less than 5 minutes ago. Wait for the Retry-After header |
{ "data": { "challengeId": 1, "status": "queued", "retryAfterSeconds": 180 } }In a 202, status is queued when this call scheduled the refresh and already_scheduled when one was already pending. Only one background refresh exists per challenge, so calling again is safe and does not stack work. The 5-minute cooldown is per challenge, not per caller: refreshing many challenges in a row is unaffected.
Metrics refresh errors
The refresh route returns a data lookup error when the post cannot be read. An age-restricted Instagram post is usually still read and returns 200, with viewCount as null. 422 POST_AGE_RESTRICTED means it could not be read at all.
Last updated on