02 Get your data in /Integrations
Instagram comments
Send comments on your Instagram professional account's posts to Dimension Labs with the universal API.
- IC Instagram commentsTickets, chats, calls, reviews
- ⟳ Your jobPull new · map fields · every 15 min
- { } Universal APIPOST /track · /trackhistorical
- Dimension LabsRead in full · enriched daily
This brings in the comments and replies on your own Instagram posts: each post as one conversation, with what the public wrote as customer messages and your caption and replies as your side. You run it on your side: a small script pulls from Meta’s Instagram API and posts each message to the universal API.
What you need
Section titled “What you need”-
An Instagram professional account (Business or Creator). Meta’s Instagram API only serves professional accounts.
-
A Meta app with one of Meta’s three Instagram API setups, and an access token for your account:
Setup Host Token Permissions Instagram API with Facebook Login (account linked to a Facebook Page) graph.facebook.comFacebook Page access token instagram_basic,instagram_manage_comments,pages_read_engagementInstagram API with Instagram Login graph.instagram.comInstagram User access token instagram_business_basic,instagram_business_manage_commentsInstagram API with Unified Login (Meta’s recommendation for new integrations) graph.facebook.comBusiness Integration System User access token instagram_basic,instagram_manage_commentsIf your role on the linked Page was granted through Business Manager, Meta also asks for
ads_managementorads_read. -
Standard Access is enough for an app that serves only accounts you own or manage. Advanced Access, through App Review, is for serving other people’s accounts.
-
A Dimension Labs API key for a new Universal API source. See Your API key.
1. Get API access in Instagram
Section titled “1. Get API access in Instagram”Meta’s steps are in the Instagram Platform overview and Get started with Facebook Login.
- Create a Meta app and add the Instagram setup you picked. Sign in as a person who manages the account and grant the permissions above.
- With Facebook Login, find your Instagram account id from the linked Page:
GET https://graph.facebook.com/v26.0/PAGE_ID?fields=instagram_business_account. Theinstagram_business_account.idis your IG User ID. A long-lived Page access token (see the Facebook Page comments guide) does not expire on a schedule. - With Instagram Login, the long-lived Instagram User token lasts 60 days. Refresh it before then with
GET https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=...(the token must be at least 24 hours old). SetIG_HOST=graph.instagram.comfor the code below. - Meta puts the API version in the path. This guide uses
v26.0, the version Meta’s reference showed when it was checked. Pin a version and move it forward on purpose.
2. Pull new comments
Section titled “2. Pull new comments”curl -G "https://graph.facebook.com/v26.0/IG_USER_ID/media" \ --data-urlencode "fields=id,caption,timestamp,media_type,permalink" \ --data-urlencode "since=1759276800" --data-urlencode "access_token=$IG_ACCESS_TOKEN"
curl -G "https://graph.facebook.com/v26.0/IG_MEDIA_ID/comments" \ --data-urlencode "fields=id,text,timestamp,from,user,parent_id,replies{id,text,timestamp,from,user,parent_id}" \ --data-urlencode "access_token=$IG_ACCESS_TOKEN"/{ig-user-id}/medialists your posts and supports time-based pagination withsinceanduntil. Meta returns at most the 10,000 most recently created media, and no Stories (they have their own endpoint)./{ig-media-id}/commentsreturns top-level comments only, newest first, at most 50 per query. Replies come only when you request therepliesfield. Meta says comments can’t be filtered by timestamp, so the code reads every comment on recent posts and skips the ones it has already sent.- Pagination: follow
paging.nextuntil it is missing, for comments and for each comment’s replies. - Your replies: Meta returns the
userfield on a comment only when your account wrote it. That is how the code tells your replies from the public’s. - Rate limits (Meta’s): Instagram calls count against Meta’s Business Use Case limits; a throttled call returns error code
80002. Meta strongly recommends webhooks for new comments to avoid rate limiting.
Storing the data. Meta’s Platform Terms apply to what you pull. They let you use a service provider such as Dimension Labs only under a written agreement that it processes the data for you and for no other purpose. They also ask you to delete Platform Data when you no longer need it for a legitimate business purpose, or when Meta or the person asks. You can’t delete individual records in Dimension Labs yourself: email support@dimensionlabs.io with the source and the comment ids for a targeted deletion.
3. Map the fields
Section titled “3. Map the fields”Each post is one conversation. Your caption is the first, outgoing message; each comment and reply from the public is incoming; your account’s replies are outgoing, in the same session.
| Dimension Labs | Notes | |
|---|---|---|
text | media caption, comment text | Skip comments with no text. |
userId | comment from.id | The Instagram-scoped id of the commenter. Your account’s caption and replies use instagram-account-IG_USER_ID. Fall back to the sessionId. |
incoming | comment user | false when user is present (your account wrote it), true otherwise. The caption is always false. |
sessionId | media id | Prefix it, for example instagram-17895695668004550. |
conversationId | media id | Same value as sessionId. |
dimensionlabs_timestamp | timestamp | Like 2017-08-31T19:16:02+0000. Convert with toMillis. A comment within a second of its post is moved a second later. |
platformJson | IG User ID, media id, media_type, comment id, parent_id | parent_id ties a reply to the comment it answers. |
Personal data. Don’t send from.username or username. Comments can hold names and handles: redact them before you send. See PII redaction.
4. Send it
Section titled “4. Send it”// instagram-comments.mjs. Environment: IG_USER_ID, IG_ACCESS_TOKEN, DL_API_KEY.// Optional: IG_HOST (default graph.facebook.com; graph.instagram.com for Instagram Login),// IG_GRAPH_VERSION (default v26.0), IG_LOOKBACK_DAYS (default 30).import { sendToDimensionLabs, toMillis } from './dimensionlabs.mjs';
const HOUR = 60 * 60 * 1000, DAY = 24 * HOUR;const sleep = (ms) => new Promise((r) => setTimeout(r, ms));const IG = process.env.IG_USER_ID;const API = `https://${process.env.IG_HOST || 'graph.facebook.com'}/${process.env.IG_GRAPH_VERSION || 'v26.0'}`;const THROTTLED = new Set([4, 17, 32, 613, 80002]); // Meta's rate limit error codes (80002 is Instagram's)const FIELDS = 'id,text,timestamp,from,user,parent_id';
// Retries rate limits (429 or a throttle error code) and server errors (5xx), honoring Retry-After when it is sent.async function get(url) { for (let attempt = 0; ; attempt++) { const u = new URL(url); if (!u.searchParams.has('access_token')) u.searchParams.set('access_token', process.env.IG_ACCESS_TOKEN); const res = await fetch(u); const body = await res.json().catch(() => ({})); if (res.ok) return body; const throttled = res.status === 429 || THROTTLED.has(body.error?.code); if ((!throttled && res.status < 500) || attempt === 5) throw new Error(`Instagram returned ${res.status}: ${JSON.stringify(body)}`); await sleep((Number(res.headers.get('retry-after')) || 30 * 2 ** attempt) * 1000); }}
// Follows paging.next until the last page.async function* pages(url) { while (url) { const page = await get(url); yield page.data ?? []; url = page.paging?.next; }}
// `user` is returned only when your own account wrote the comment.const mine = (c) => Boolean(c.user);
// Sends every caption, comment and reply on your media created at or after `since` (Unix ms). Returns the new `since`.export async function syncInstagramComments({ since, historical = false }) { let newest = since; const from = historical ? since : since - Number(process.env.IG_LOOKBACK_DAYS || 30) * DAY; // comments arrive on older media too const send = (msg) => sendToDimensionLabs(msg, { historical: historical || msg.dimensionlabs_timestamp < Date.now() - HOUR }); const mq = new URLSearchParams({ fields: 'id,caption,timestamp,media_type,permalink', since: String(Math.floor(from / 1000)) }); for await (const media of pages(`${API}/${IG}/media?${mq}`)) { for (const m of media) { const sessionId = `instagram-${m.id}`; const mediaTs = toMillis(m.timestamp); const base = { sessionId, conversationId: sessionId, platformJson: { igUserId: IG, mediaId: m.id, mediaType: m.media_type } }; if (m.caption?.trim() && mediaTs >= since) { await send({ ...base, text: m.caption.trim(), userId: `instagram-account-${IG}`, incoming: false, dimensionlabs_timestamp: mediaTs, platformJson: { ...base.platformJson, kind: 'caption' } }); newest = Math.max(newest, mediaTs); } const cq = new URLSearchParams({ fields: `${FIELDS},replies{${FIELDS}}` }); for await (const comments of pages(`${API}/${m.id}/comments?${cq}`)) { for (const top of comments) { // Replies come with the comment; if there are more than one page of them, follow the rest. const replies = [...(top.replies?.data ?? [])]; for (let next = top.replies?.paging?.next; next;) { const p = await get(next); replies.push(...(p.data ?? [])); next = p.paging?.next; } for (const c of [top, ...replies]) { const created = toMillis(c.timestamp); if (created < since || !c.text?.trim()) continue; // already sent, or no text newest = Math.max(newest, created); await send({ ...base, text: c.text.trim(), incoming: !mine(c), userId: mine(c) ? `instagram-account-${IG}` : c.from?.id ? `instagram-${c.from.id}` : sessionId, dimensionlabs_timestamp: Math.max(created, mediaTs + 1000), platformJson: { ...base.platformJson, kind: c.parent_id ? 'reply' : 'comment', commentId: c.id, parentId: c.parent_id } }); } } } } } return newest;}On every run the code reads the posts from the last 30 days (IG_LOOKBACK_DAYS) and all their comments, because people keep commenting on older posts. It sends only what was written since the last run. A busy account with a long lookback uses many calls: shorten the lookback, or move new comments to Meta’s webhooks.
5. Run it
Section titled “5. Run it”Save the code as instagram-comments.mjs in a project set up as in Set up your project, and run it with the scheduled sync runner: import { syncInstagramComments as sync } from './instagram-comments.mjs';.
- History first:
node run.mjs 2025-01-01sends every caption, comment and reply on posts since that date to/trackhistorical. Then start a backfill from the Backfill tab. - Then every 15 minutes: the scheduled job sends new comments to
/track, and they are enriched automatically every day. Anything more than an hour old when the job sees it goes to/trackhistorical, and needs a backfill. - Re-runs are safe: duplicates are removed automatically.
When Meta throttles the app, the code waits and tries again. If you have comments in a file instead, upload it and check it first with the file checker, then run a backfill.
Instagram reference
Section titled “Instagram reference”- Instagram Platform overview
- Comment moderation
- IG Media comments
- IG Comment
- IG Comment replies
- IG User media
- Get started with Facebook Login
- Refresh access token
- Rate limits
- Meta Platform Terms
Checked against Meta’s documentation on 2026-10-26.