People looking for an Instagram comment scraper usually want the text under one post, with the like count on each comment, as JSON they can store. That is one GET.
GET /v1/instagram/post/comments returns the first page of public comments on a post or reel. The reference describes that page as about 14 comments, in Instagram's own order. Each comment includes likes. Reply counts are null, because Instagram sends them empty to logged-out visitors.
The endpoint is in beta. A field the platform does not provide comes back as null. The API does not fill it in.
Send the key#
Put the key in the x-api-key header. It is not accepted in the query string, so it does not land in a log line or a browser history. A missing, malformed or revoked key is 401 unauthorized. A suspended key or team is 403 forbidden. Too few credits is 402 insufficient_credits. The shapes are on the authentication page.
Parameters#
Send one of url or id.
url is a post or reel link: /p/..., /reel/... or /tv/..., on instagram.com or instagr.am. id is the shortcode or the numeric media id. shortcode and media_id are aliases of id.
include_replies is accepted and ignored. Replies need a login, so the flag changes nothing. cursor is on the parameter list because list endpoints share it. On this endpoint has_more is always false. There is no logged-out way to the next page, so a cursor will not uncover the rest of the thread.
trim drops null fields and extra. raw also returns the platform object. max_age_days takes a number from 0 to 30. Absent or 0 means fetch fresh. A cached copy inside the window costs 0 credits. This endpoint keeps results 30 days. Nothing comes from the cache unless you ask. See caching.
The recorded request uses a public reel URL.
curl -G "https://api.socialscrape.io/v1/instagram/post/comments" \
-H "x-api-key: $SOCIALSCRAPE_API_KEY" \
--data-urlencode "url=https://www.instagram.com/reel/DfF3eqGSKKc/"import os
import requests
params = {
"url": "https://www.instagram.com/reel/DfF3eqGSKKc/",
}
res = requests.get(
"https://api.socialscrape.io/v1/instagram/post/comments",
headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
params=params,
)
body = res.json()
if body["ok"]:
print(body["data"])
else:
print(body["error"]["code"], body["error"]["message"])const url = new URL("https://api.socialscrape.io/v1/instagram/post/comments");
url.search = new URLSearchParams({
"url": "https://www.instagram.com/reel/DfF3eqGSKKc/"
}).toString();
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();
if (body.ok) {
console.log(body.data);
} else {
console.error(body.error.code, body.error.message);
}Read body["ok"] before you touch data. The Python and TypeScript above already branch on error.code and error.message. Retry only when error.retryable is true. The message is for humans.
What the response contains#
A success envelope has ok, credits_charged, credits_remaining, cached, cached_at and request_id, then data. List endpoints also return cursor and has_more on the envelope. For this call both are empty of a next page: has_more is false and cursor is null.
data.object is list. Comments are data.items. Each item is a comment:
objectiscommentandplatformisinstagram.idis the comment id.urlpoints at that comment.post_idis the shortcode.parent_idis null on a top-level comment.authorcarriesid,handle,name,url,avatar_urlandverified.textis the comment body.published_atis when it was posted.stats.likesis the like count.stats.repliesis null.stats.dislikesis null.is_pinnedmay be null.is_by_creatoris true when the account that owns the post wrote the comment.
Sample. The recorded response is shortened and anonymised, so the author handle in that file is a placeholder (sample_user) and is not someone to store as a real account. stats.likes in that sample is a small integer, and stats.replies is null, which is the real shape, not an anonymisation. Do not treat sample balances (credits_remaining in the recording) as your account. Your balance is whatever the live envelope returns.
The comment total is not on this payload. It is stats.comments on the post, from GET /v1/instagram/post, using the same URL or shortcode. That call returns the caption, counts, media, owner, tagged users and, for a video, a view count taken from the embed page. extra.views_source is embed_views in that case, and the reference says it differs from the play count of the reels tab. include_play_count set to false skips that extra fetch.
curl -G "https://api.socialscrape.io/v1/instagram/post" \
-H "x-api-key: $SOCIALSCRAPE_API_KEY" \
--data-urlencode "url=https://www.instagram.com/reel/DfF3eqGSKKc/"import os
import requests
params = {
"url": "https://www.instagram.com/reel/DfF3eqGSKKc/",
}
res = requests.get(
"https://api.socialscrape.io/v1/instagram/post",
headers={"x-api-key": os.environ["SOCIALSCRAPE_API_KEY"]},
params=params,
)
body = res.json()
if body["ok"]:
print(body["data"])
else:
print(body["error"]["code"], body["error"]["message"])const url = new URL("https://api.socialscrape.io/v1/instagram/post");
url.search = new URLSearchParams({
"url": "https://www.instagram.com/reel/DfF3eqGSKKc/"
}).toString();
const res = await fetch(url, {
headers: { "x-api-key": process.env.SOCIALSCRAPE_API_KEY! },
});
const body = await res.json();
if (body.ok) {
console.log(body.data);
} else {
console.error(body.error.code, body.error.message);
}One post plus one comment page is two successful requests, so two credits if both miss the cache.
Cost#
A successful comment page is 1 credit. You do not pay for a second page, because the second page is not available. Failures are free. Cached repeats are free when max_age_days is set.
New accounts get 100 credits once, with no card. Pack credits never expire. Monthly plan credits reset each month and do not roll over. The current packs and plans are on pricing. The rules, including which credits are spent first, are in credits.
Limits#
You get the first page Instagram shows a logged-out visitor, about 14 comments, in Instagram's order. You do not get a sort by likes. You do not get replies. You do not get the rest of a post that has hundreds of comments. If you need the size of the thread, read stats.comments on the post and treat the gap as data you do not have.
invalid_params and unsupported_url mean the link or the id was wrong. not_found means the post is missing. Do not retry those. rate_limited is 429, retryable, and costs 0 credits. The retry-after header says how many seconds to wait. The limit is per team. On free credits it is 30 requests a minute. A paid pack is 300. Monthly plans run from 120 on Starter to 1,200 on Scale. The table is on rate limits. Other codes are on errors.
The API is in early access, and this endpoint is beta, so later responses may gain fields. They will not start inventing reply counts.
Next#
Parameters, the sample and every error for this call are on the Instagram comments API page and in the reference. The same request in more languages is the tutorial, with a Python script and a JavaScript script. The post lookup is Instagram post or reel details. Every Instagram route is on the Instagram API hub.
If the job is a keyword rather than one URL, search reels first and then open comments on the ones you keep. That path is social listening.