Instagram profiles, posts, reels, comments, stories, highlights, followers, hashtags, locations, and audio through one API key
Public Instagram data without a Meta app review: profiles and their whole feed, reels with view and share counts, comment threads, story trays and saved highlights, the follower graph, hashtag and location feeds, and the audio library. Handles are passed without the leading @.
Base URL: /v1/instagram/...
Share counts and follower lists come from a heavier upstream than the rest of
the platform. /v1/instagram/post carries no share count and no cheap
endpoint does. Use /v1/instagram/post/stats for one post, or
/v1/instagram/profile/posts/full and /v1/instagram/profile/reels/full for
a whole feed. View counts are different: they stay on the 1-credit reads, with
post, profile/posts, and profile/reels all carrying them on video.
Quickstart
1. Fetch a profile
curl "https://www.socialcrawl.dev/v1/instagram/profile?handle=instagram" \
-H "x-api-key: YOUR_API_KEY"2. Fetch their content
curl "https://www.socialcrawl.dev/v1/instagram/profile/posts?handle=instagram" \
-H "x-api-key: YOUR_API_KEY"3. Read computed fields
When an endpoint supports a computed field and the required source inputs are present, the unified response includes that optional field. Depending on the endpoint, optional fields can include engagement_rate, language, content_category, and estimated_reach. See Computed fields for formulas, clamping rules, and null semantics. Where an endpoint supports them, list rows also carry judged labels (computed.labels, computed.relevance) by default at no extra credits; see Labels.
Accounts
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/profile | 1 | Bio, exact integer follower count, following and post counts, avatar, verification | handle, trim |
GET /v1/instagram/basic-profile | 1 | Name and picture only, for when all you hold is a numeric id | userId |
GET /v1/instagram/profile/full | 5 | Profile plus recent posts plus computed analytics in one flat call | handle, posts (1-100, default 25, at most 12 per call), include |
GET /v1/instagram/engagement | 5 | An overall engagement rate, totals, and a per-post breakdown | handle |
GET /v1/instagram/followers | 5 or 10 | Who follows this account, about 50 a page | handle or user_id, cursor, coverage |
GET /v1/instagram/following | 5 or 10 | Who this account follows, about 50 a page | handle or user_id, cursor, coverage |
GET /v1/instagram/similar | 5 | The accounts Instagram itself suggests as comparable, one fixed list | handle or user_id |
GET /v1/instagram/user/embed | 1 | An embeddable HTML snippet for dropping a profile onto your own page | handle |
profile also returns author.last_post_at, the publish time of the newest post among the recent posts the profile read carries, as an ISO 8601 timestamp, at no extra cost. Pinned posts count, and the newest is taken whatever the order. It is null for a private account, for an account with no posts, and when the profile read returned no recent posts, so treat null as unknown rather than as inactive.
profile/full collapses the first two Quickstart calls into one and adds two average engagement rates, posting cadence, top post, and format mix. One call reads one page of recent posts, which on Instagram is 12, and computes everything over that page: posts can shorten the window but a value above 12 still returns 12, with a _warnings note, and posts_cursor sent back as cursor reads the next page for another 5 credits. computed.avg_engagement_rate is view-based, the mean of (likes + comments + shares) / views per post, and Instagram publishes no view count on photos and carousels, so on a mostly-photo grid it is null. computed.avg_engagement_rate_by_followers divides the mean likes and comments in that window by the follower count instead, the same formula as computed.engagement_rate on profile. It is null when the follower count is missing and when no post in the window carries a like or comment count, for example an account that hides its likes. include=computed only omits the raw posts array from the response; the metrics are computed from the same posts either way. If the posts leg fails the profile still comes back, with the post-dependent metrics null.
Posts and reels
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/profile/posts | 1 | The cheap feed pull: likes, comments, and views on video | handle, next_max_id, trim, label, judgments |
GET /v1/instagram/profile/reels | 1 | The same for reels | handle or user_id, max_id, trim, label, judgments |
GET /v1/instagram/profile/posts/full | 5 to 25 | The feed plus a per-item share count and a coverage figure | handle or user_id, limit (max 50) |
GET /v1/instagram/profile/reels/full | 5 to 25 | The same for reels | handle or user_id, limit (max 50) |
GET /v1/instagram/post | 1 | Caption, media, tagged users, engagement, and the play count on video | url, download_media |
GET /v1/instagram/post/stats | 5 | Adds the paper-plane share count on video | url |
GET /v1/instagram/post/likers | 5 | A ranked sample of who liked a post | url |
GET /v1/instagram/media/transcript | 10 | The spoken words in a video or reel | url |
GET /v1/instagram/tagged | 5 | Posts other people tagged an account in, a different question from what it posted | handle or user_id, cursor |
The two full variants are metered: they consume upstream pages of 12 items and bill 5 credits per page, from 5 up to 25 for a limit=50 walk. Both need a handle for the share leg. A user_id-only call still returns views, likes and comments, with shares null and a partial refund.
Instagram removed play counts from its public web pages in August 2026, so post now fills the count from a second source inside the same call. You still make one request and pay one credit.
Comments
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/post/comments | 5 | A post's comment section, ranked by popularity by default | url, sort, cursor, label, judgments |
GET /v1/instagram/post/comment/replies | 1 | One comment's replies, not a second copy of the section | url and comment_id, both required; optional label, judgments |
GET /v1/instagram/comment | 5 or 15 | One named comment, or comments matching an author or a snippet | comment_url, or post_url plus comment_id |
Pass sort=recent whenever you intend to walk a whole thread. Instagram only ranks the most-liked head of the list, so a top walk starts repeating comments once you page past it. Page deeper with the next_cursor from each response.
GET /v1/instagram/comment is a bounded server-side scan rather than a direct fetch, because Instagram exposes no fetch-one-comment read. It costs 5 credits, or 15 with deep_scan for comments buried deep in a large thread. Passing back the lookup.position_hint from an earlier call replays the last-known sort chain first, which makes re-checking a comment you have already found much cheaper.
Stories and highlights
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/stories | 5 | The tray a user has live right now | handle or user_id |
GET /v1/instagram/story/download | 5 | One story's full-resolution media | user_id and story_id |
GET /v1/instagram/highlights | 1 | The saved collections pinned to a profile | handle or user_id |
GET /v1/instagram/highlight/detail | 1 | One highlight opened by id | id |
An account with nothing live returns an empty list rather than an error. Highlights are the permanent version of the same thing.
Search and discovery
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/search | 1 | Instagram's own mixed search box: accounts, hashtags, and places | query |
GET /v1/instagram/search/profiles | 1 | Accounts found by bio or caption keyword | query, cursor |
GET /v1/instagram/search/popular | 1 | The popular-posts feed for a keyword | query, cursor |
GET /v1/instagram/search/reels | 1 | Reels matching a keyword or phrase, page-paginated | query, date_posted, page, region, country, include, label, relevance, judgments |
GET /v1/instagram/search/hashtag | 5 | A tag feed, with type selecting top, recent, or clips | hashtag, type, cursor |
GET /v1/instagram/search/location | 5 | Turns a place name into location.pk for location_id | query |
GET /v1/instagram/location/posts | 5 | What was posted at that place, newest first, about 60 a page | location_id, cursor |
GET /v1/instagram/reels/trending | 5 | A global sample of Instagram's public trending page | none |
GET /v1/instagram/username-suggestions | 5 | Invented available handle ideas from a keyword | query |
search/profiles reads Google's index of Instagram rather than Instagram's own account search, so treat it as discovery rather than a lookup. username-suggestions is unrelated to it despite the name: it does not look anything up. reels/trending takes no parameters, sends a small batch per call, and repeats items, so call again to see more.
Neither trending endpoint takes a region. Instagram builds the trending reels feed and the trending music chart for the account viewing them, not for a place, so there is no country to pass and none is planned. For posts from one region, look up places with search/location (a city, a neighbourhood, a venue) and page each place's posts with location/posts, or send region to search/reels (below).
Hashtag feeds: only recent pages
GET /v1/instagram/search/hashtag takes type=top (the default), type=recent, or type=clips. Only type=recent paginates: forward the next_cursor from each response and send type=recent again with it. top and clips are one ranked page each, so they come back with has_more: false and no cursor. To cover more ground with those rankings, query more hashtags rather than more pages.
Reel search ordering is relevance, not a stable list
GET /v1/instagram/search/reels returns Instagram's own relevance ranking for the keyword, and that ranking is live. The same query re-run minutes later can come back with a different, partially overlapping set of reels, and pages can overlap at their boundaries. No parameter pins an ordering, and date_posted narrows the window rather than sorting it.
date_posted accepts last-week, last-month, and last-year. last-day and last-hour stopped being served around 26/08/2026, and a request carrying either is rejected before any call is made, at no charge, with an error that names the three values that work. Note that a filtered search returns engagement.views as null, because the only surface that offers a date filter no longer carries play counts; if you need both a date window and view counts, omit date_posted and filter on published_at yourself.
Build on that rather than against it. Rank on engagement.views in your own code instead of trusting result order, deduplicate by post.id across pages and across runs, and if you need a stable view of a keyword over time, accumulate results into your own store across repeated runs. For a creator's complete back catalogue in a fixed order, walk profile/reels instead: a profile listing is stable in a way a relevance search cannot be.
Local results and the creator card on reel search
GET /v1/instagram/search/reels takes three optional parameters. Each works alone, and they combine. Without them the call is unchanged.
curl "https://www.socialcrawl.dev/v1/instagram/search/reels?query=restaurantes®ion=ES&country=ES" \
-H "x-api-key: YOUR_API_KEY"region(ES,MX,DE,BR,KRorFR) localises the results. Instagram does not rank search by country, so the market's own name is added to your query:fitnesswithregion=ESsearchesfitness españa. A query that already names the market, or is already written in the market's own script (Hangul for KR), which Instagram localises by itself, is sent as it is.data.region.query_sentshows the query that ran. Same price as a plain call. It favours creators from that market but does not promise every creator is from it.include=creatoradds each reel's creator card from the creator's About this account panel:post.ext.author_country,author_followers,author_following,author_posts_count,author_public_emailandauthor_public_phone. It never drops, adds or reorders rows. 2 extra credits per creator looked up; two reels by one creator are one lookup, a creator that cannot be found is refunded, and a creator looked up in the last 15 minutes is free.data.hydrationreports the lookups and credits.countrykeeps only the reels whose creator declares that market, and reports what it dropped indata.country(kept,dropped_other,dropped_unknown). It adds the creator card at the same price, charged for every creator looked up whether their reels are kept or not, and localises the query too whenregionis not sent. A page where no creator matches returnsitems: []with the warningcountry_no_matchindata._warnings, and its lookups are still billed. A page left empty only because the creator lookups failed costs nothing.
author_country is the country the creator declares on Instagram, not where the reel was filmed. A null means Instagram does not publish one for that creator, and nothing is guessed. The same parameter names mean the same thing on TikTok search; only the way region works differs, because TikTok searches from the market and Instagram needs the query localised.
Topics about a place, such as travel or eSIMs, are often made by visitors, so region alone finds fewer local creators there. A phrasing locals use helps (esim móvil rather than esim), and country keeps only the market's creators. Any other market code, or a typo in any of the three, is refused at no charge with the list of valid values.
post.ext.author_followers is null on a plain call, because Instagram stopped sending follower counts in its search payload in August 2026. include=creator fills it. For creators you already hold, GET /v1/instagram/profile returns author.followers, and POST /v1/prism/profiles resolves up to 50 handles per call at 1 credit per resolved profile, failed rows refunded. One call can mix platforms, so TikTok and Instagram creators resolve together. Match rows to your input by each row's index.
Audio
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/instagram/search/music | 5 | Tracks matching a name, each with an audio id | query, cursor |
GET /v1/instagram/music/trending | 5 | A chart of licensed tracks trending now | none |
GET /v1/instagram/audio/reels | 1 | The reels using one sound, cursor-paged | audio_id, cursor |
audio_id is the number in an instagram.com/reels/audio/{audio_id}/ URL. That chain is how you measure a sound's spread rather than its chart position.
All endpoints
엔드포인트 38개를 제공해요.
| 엔드포인트 | 경로 | 크레딧 등급 |
|---|---|---|
| List Instagram reels using an audio track | /v1/instagram/audio/reels | standard (1cr) |
| Get Instagram basic profile | /v1/instagram/basic-profile | standard (1cr) |
| Get Instagram highlight detail | /v1/instagram/highlight/detail | standard (1cr) |
| List Instagram story highlights | /v1/instagram/highlights | standard (1cr) |
| Get Instagram post details | /v1/instagram/post | standard (1cr) |
| List replies under an Instagram comment | /v1/instagram/post/comment/replies | standard (1-5cr)metered |
| Get Instagram user profile | /v1/instagram/profile | standard (1cr) |
| Get Instagram account transparency details | /v1/instagram/profile/about | standard (1cr) |
| Instagram profile, recent posts, and computed analytics in one call. | /v1/instagram/profile/full | standard (5cr) |
| List Instagram user posts | /v1/instagram/profile/posts | standard (1-17cr)metered |
| List Instagram user reels | /v1/instagram/profile/reels | standard (1-18cr)metered |
| Search Instagram accounts, hashtags, and places | /v1/instagram/search | standard (1cr) |
| Search popular Instagram posts | /v1/instagram/search/popular | standard (1-13cr)metered |
| Search Instagram profiles by keyword | /v1/instagram/search/profiles | standard (1-25cr)metered |
| Search Instagram reels | /v1/instagram/search/reels | standard (1-69cr)metered |
| Get Instagram user embed HTML | /v1/instagram/user/embed | standard (1cr) |
| Look up one Instagram comment by URL or id | /v1/instagram/comment | advanced (5-15cr)metered |
| Get Instagram engagement statistics | /v1/instagram/engagement | advanced (5cr) |
| List Instagram followers | /v1/instagram/followers | advanced (5-10cr)metered |
| List Instagram following | /v1/instagram/following | advanced (5-10cr)metered |
| List recent posts at an Instagram location | /v1/instagram/location/posts | advanced (5cr) |
| Get Instagram post on-screen text | /v1/instagram/media/screen-text | advanced (5cr) |
| List trending Instagram music | /v1/instagram/music/trending | advanced (5cr) |
| List Instagram post comments | /v1/instagram/post/comments | advanced (5-19cr)metered |
| List Instagram post likers | /v1/instagram/post/likers | advanced (5cr) |
| Get Instagram post stats including the share count | /v1/instagram/post/stats | advanced (5-9cr)metered |
| Instagram posts with views, likes, comments, and per-post share counts where available, in one call. | /v1/instagram/profile/posts/full | advanced (5-25cr)metered |
| Instagram reels with views, likes, comments, and per-reel share counts where available, in one call. | /v1/instagram/profile/reels/full | advanced (5-25cr)metered |
| Get trending Instagram reels | /v1/instagram/reels/trending | advanced (5cr) |
| Search Instagram posts by hashtag | /v1/instagram/search/hashtag | advanced (5cr) |
| Search Instagram locations | /v1/instagram/search/location | advanced (5cr) |
| Search Instagram music | /v1/instagram/search/music | advanced (5cr) |
| List similar Instagram accounts | /v1/instagram/similar | advanced (5-85cr)metered |
| List an Instagram user's active stories | /v1/instagram/stories | advanced (5cr) |
| Download a single Instagram story | /v1/instagram/story/download | advanced (5cr) |
| List posts an Instagram user is tagged in | /v1/instagram/tagged | advanced (5cr) |
| Get Instagram username suggestions | /v1/instagram/username-suggestions | advanced (5cr) |
| Get Instagram media transcript | /v1/instagram/media/transcript | premium (10cr) |
Platform notes
Media URLs expire. The media_urls on any post are short-lived signed CDN links. For archiving, call post with download_media=true and read the durable links from data.post.ext.download_media_urls, each entry carrying post_id, cdn_url, type, and cached. It adds a few seconds of latency while the media is fetched.
Likers are a ranked sample, now about a thousand rows. post/likers returns a ranked slice of a post's likers in one response, regardless of how many likes the post has, at 5 credits. On 14/09/2026 a post with 94,090 likes returned 1,019 of them; earlier releases returned roughly the first 100. The full like count is in the same response as data.total, and Instagram exposes no cursor on this surface, so the list is a sample rather than the whole thing.
Follower and following lists, and coverage=full. A plain walk of followers or following can end with has_more: false before the profile's count. On most accounts it now ends within a few rows of that count; on large verified accounts Instagram caps every route at about 50 rows with no cursor, and a plain walk ends there. data.total on every page carries the profile's own count, so compare the rows you collected against it. Send coverage=full with handle to walk a merged list instead: each page is drawn from several reads of the list and carries no account twice inside a page, until the rows reach the count or no read has more. Across consecutive pages a handful of accounts can repeat on the pages one read covered on its own; wherever a single read cannot cover the list, the merged walk runs instead and never returns an account it has already given you. A full-coverage page is quoted at 10 credits instead of 5 and settled back down to 5 on any page a single read covered in full, so the mode costs its full price only on the accounts that need it. A page carries about 50 accounts and usually takes about 8 seconds, occasionally up to 45. A full list therefore costs at most roughly its account count divided by 50, times 10 credits: on 13/09/2026 a following list of 2,652 took 54 pages and a follower list of 2,360 took 59, since the later pages of a follower walk add fewer new accounts. Keep coverage=full on every call of the walk, since its cursor only continues that mode, and retry a 503 with the same cursor.
curl "https://www.socialcrawl.dev/v1/instagram/following?handle=billboard&coverage=full" \
-H "x-api-key: YOUR_API_KEY"A private account is a 404. followers and following return 404 RESOURCE_NOT_FOUND with details.reason: account_private, refunded, when the account exists but its lists are hidden.
Like and comment counts are Instagram's own. For a post also shared to Facebook, the likes and comments it collected on Facebook are not included in post.engagement, whichever source answers. That is the figure instagram.com shows under the post. Views follow the same rule.
Photo posts have no views and no shares. Both come back null on post/stats, which is Instagram's behaviour rather than a gap.
A location grid pages back in time. location/posts returns about 60 posts a page, newest first, and pagination.next_cursor fetches the next, older page until has_more is false. How far one page reaches depends on the place: a busy landmark fills a page in about a day, a neighbourhood covers a week or two, and a quiet spot can span years. Instagram orders the grid by when each post was created, so a scheduled post that went live later sits among older posts, while its published_at shows the real publish time. On 13/09/2026 that was about 1 post in 75, up to 13 days apart. To collect a time window, keep paging until a whole page is older than the window's start and filter on published_at, rather than stopping at the first older post. Each page is a separate call at 5 credits, and a page takes about 5 to 12 seconds.
music/trending reflects the viewing account's market. Instagram serves its music chart to an account, and some accounts are shown Instagram's royalty-free sound library instead of a chart. The endpoint returns only a licensed chart; when none comes back it answers 503 with your credits refunded, so retry after a minute. The market a chart belongs to is not exposed and cannot be selected.
safe_url=true where you are embedding. Several endpoints accept it and return URL-safe profile picture and media links, suitable for dropping straight into a page.
Carousels return every slide. A sidecar post's media_urls is the full array, not just the cover image.
trim=true costs the same and returns the full list. profile, profile/posts, and profile/reels accept it and return the same rows at the same price. On the two feed endpoints the only differences are that post.ext.coauthors is omitted and post.flags.pinned is null, because the trimmed record carries neither signal; every other field is unchanged.
post.id is the exact 19-digit media id. On profile/reels, tagged, and stories the last two or three digits were rounded on every row in earlier releases, and tagged page 2 repeated page 1. If you stored ids from those three endpoints before this change, re-sync them. Ids from profile/posts, search/hashtag, and location/posts were always exact.
A handle that does not exist is a 404, not an empty list. profile, followers, and following return 404 RESOURCE_NOT_FOUND with details.reason: handle_unresolved, refunded, when the account cannot be resolved. An existing account whose list is empty still returns 200 with items: [].
Labels
These lists label their rows by default, at no extra credit. On /v1/instagram/post/comments and /v1/instagram/post/comment/replies, each comment carries sentiment, question, purchase_intent and complaint on computed.labels. On /v1/instagram/profile/posts, /v1/instagram/profile/reels and /v1/instagram/search/reels, each row carries sponsored, intent and niche. /v1/instagram/search/reels also scores each row's relevance to your query on computed.relevance, without dropping or reordering anything; add relevance=filter to drop the rows that are about something else, still free. Send judgments=off for the page without them. See Labels for the field shapes, rows still pending, and the labels you can add with label=.
Next steps
Pagination
Cursor and page walks, and what has_more actually means.
Credits
What each tier costs and when a call is refunded.
API reference
Every parameter and response field, endpoint by endpoint.
TikTok
The other short-form surface most crawls pair with Instagram.
Threads
Meta's text network, same canonical Post object.
