GET/v1/instagram/channel-posts1 credit per post

Instagram Profile Posts API

Give it a public Instagram handle and get that account's recent feed posts back as an array of JSON objects — caption, engagement counts, timestamp, media type and thumbnail for each one. One call returns one array sized by `limit` — there is no cursor to follow — and billing is per post returned, at 1 credit each, so a default call of 20 costs 20 credits. No Graph API app review, no Business account linkage, no Page token.

Request

parametertypedescription
handlestring, requiredThe Instagram username, without the @.
limitintegerItems per page, up to 50 synchronously. Above that the call becomes a job.
cursorstringmeta.next_cursor from the previous page.
freshbooleanSkip the stored copy and read live, at the same price.
fieldsstringComma-separated field paths to keep.
formatjson, csvCSV flattens each item to one row.

Billing and freshness

1 credit per post. Failed calls are never charged, and empty results are free up to 25 a day. Stored copies are refreshed every 3 hours.

What you get back

An array under `data`, newest post first, one object per post on the account's grid. Each object carries the shortcode as its id, the canonical permalink, the caption text, like and comment counts, the publish timestamp, a thumbnail URL and a small nested author block. Reels and video posts also carry a play count and a runtime.

The shape is deliberately the same object the Instagram Stats API returns for a single URL, so a list call and a detail call are interchangeable in your storage layer. Upsert on `id` and you can mix the two without writing a second mapper.

You control how much comes back with `limit`, which defaults to 20 and accepts anything from 1 to 500. Requests above 50 are executed as an asynchronous job instead of being answered inline. There is no cursor, page token or offset in the response, so how far back you reach is decided entirely by `limit` on a single call. Cost scales with what you actually receive: ask for 50 posts, pay for 50 posts. Ask for 5 and pay for 5.

GET /v1/instagram/channel-posts
curl "https://api.instadata.dev/v1/instagram/channel-posts?handle=nasa&limit=10" \
  -H "Authorization: Bearer ik_live_..."
Example response
{
  "data": [
    {
      "id": "C8xQvZ2sVAb",
      "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/",
      "caption": "Three settings you should change today",
      "posted_at": "2026-07-03T12:00:00Z",
      "metrics": {
        "likes": 55210,
        "comments": 981
      }
    },
    {
      "id": "C8xQvZ2sVAb1",
      "url": "https://www.instagram.com/reel/C8xQvZ2sVAb/",
      "caption": "Three settings you should change today",
      "posted_at": "2026-07-03T12:00:00Z",
      "metrics": {
        "likes": 55210,
        "comments": 981
      }
    }
  ],
  "meta": {
    "count": 2,
    "limit": 20,
    "item": "post"
  },
  "request_id": "req_01JZX4M8Q2TE9W"
}

Fields

fieldtypenote
idstringThe post shortcode (the segment after /p/ or /reel/ in the URL). Stable for the life of the post — use it as your primary key.
urlstringCanonical instagram.com permalink, reconstructed from the shortcode when the upstream page omits it.
typestring|nullMedia kind as Instagram labels it, e.g. Image, Video, Sidecar, Clips. Null when the page does not expose a product type.
captionstring|nullCaption text exactly as posted, including hashtags and line breaks. Null for posts published with no caption.
viewsnumber|nullPlay count for video and reel posts. Always null on still images and carousels — Instagram does not publish a view count for those.
likesnumber|nullPublic like count. Null when the account has hidden like counts on that post, which is a per-post setting.
commentsnumber|nullNumber of comments. Null when comments are switched off. Fetch the text itself with the Instagram Comments API.
sharesnullAlways null on Instagram. The field exists so post objects share one shape across endpoints; Instagram does not publish a public share count on post pages. The reel-shares endpoint reads it through a separate upstream add-on.
duration_secnumber|nullVideo runtime in seconds. Null for images and carousels.
posted_atstring|nullISO 8601 UTC publish timestamp. Sort on this rather than trusting array order if you merge results from more than one call.
authorobjectNested { handle, name } for the posting account. Follower and post totals are not here — those come from the Instagram Channel Stats API.
thumbnailstring|nullDirect URL to the display image or video cover frame. Instagram CDN links expire, so download the bytes if you need them to last.
hashtagsstring[]|nullHashtags associated with the post as bare strings. Null when the upstream page exposes none, which happens even on captions that visibly contain tags — parse `caption` yourself if you need them reliably.

Reach for it when

  • Building a competitor feed: run a handful of handles nightly, upsert on id, and you have a searchable archive of everything your category posts.
  • Influencer vetting before a deal — pull the last 30 posts and check whether likes are steady or propped up by one outlier, and how often the account actually publishes.
  • Content research at the account level: sort a creator's posts by likes to see what their audience rewards, then read the captions of the top ten.
  • Backfilling a dashboard for accounts you do not own and therefore cannot reach through Instagram's own Graph API.
  • Feeding a downstream pipeline — take the `url` of each returned post and pass it to the Instagram Comments API or a transcript step.

Not the right call when

  • You only care about one specific post and already have its link — use the Instagram Stats API, which takes a URL and costs 1 credit instead of billing per item.
  • You want reels specifically. This endpoint returns the main grid; the Instagram Channel Reels API is scoped to reels and is the cleaner call for short-video analysis.
  • You want follower count, bio, verification status or whether the account is private — that is the Instagram Channel Stats API, one call per handle.
  • You want posts matching a topic rather than posts from one account — start with the Instagram Reels Search API instead of iterating handles. It is keyword-driven and covers reels only; we do not ship a hashtag endpoint for Instagram.

Things to know

Private accounts return nothing, and that is by design

We read Instagram the way a logged-out visitor does. If an account is private, its grid is not public, so there is nothing to return and the call errors rather than handing back an empty array dressed up as success. Failed calls are not charged.

Check `is_private` on the Instagram Channel Stats API first if you are working through a list of handles of unknown quality. One stats call is cheaper than a failed page attempt plus a retry.

No pagination, and why cost is per post

Billing is 1 credit per post returned, not per request. A `limit=50` call costs 50 credits; a `limit=5` call costs 5. Set `limit` to what you will actually store, because you pay for every item that comes back whether or not you read it.

There is no cursor, page token or offset in the response. One call returns one array and that is the end of it. To reach further back you raise `limit` and call again from the top — which re-fetches everything you already had, and bills you for it a second time. Upsert on `id` when you merge.

`limit` accepts 1 to 500, and anything above 50 runs as an asynchronous job rather than an inline response. Instagram will sometimes return fewer posts than you asked for on accounts with a short or hard-to-reach back catalogue; that is the end of what is publicly reachable, not a bug, and you are only billed for what arrived.

Null is normal on Instagram

Several fields can be null because Instagram lets creators hide like counts per post, disable comments, and post without captions. `views` is null on every image and carousel, and `shares` is null on everything — Instagram publishes no public share figure.

Do not treat a null count as zero in an average. Filter those posts out of the denominator or your engagement rate will drift low on accounts that hide likes.

Thumbnails are borrowed, not hosted

The `thumbnail` URL points at Instagram's CDN. Those links carry signed expiry parameters and stop working after a while, so storing the string and rendering it a week later will give you broken images. Fetch the bytes at ingest time and put them in your own bucket if the image needs to persist.

Medium cache window

Responses cache in the medium class. Calling the same handle with the same limit twice in a row returns the second one from cache with `x-cache: hit`, in milliseconds, at the same credit cost. Add `fresh=true` when you specifically need to catch a post published minutes ago — a daily competitor sweep does not need it.

Questions

How do I get someone's Instagram posts without the Graph API?

Send a GET to /v1/instagram/channel-posts with the handle and your API key. Instagram's Graph API only reaches accounts you own or manage, and requires a Business account, a linked Facebook Page and app review. This endpoint reads the public grid of any public account, so competitor and creator research works without any of that setup.

How much does it cost to pull 100 Instagram posts?

100 credits. Billing is 1 credit per post returned, so 100 posts is 100 credits — about $0.50 on the monthly plan at $0.005 per credit, or $0.45 on annual. That is one call with `limit=100`; because the limit is above 50 it runs as an asynchronous job rather than returning inline. Failed calls are not charged.

How far back can I scrape an Instagram account's posts?

As far as a single call with a high `limit` reaches, which varies by account. The maximum `limit` is 500, and there is no cursor to continue past whatever that call returns. Recent posts are reliable; the deeper into a large back catalogue you ask, the more likely Instagram returns fewer items than you requested. There is no fixed cutoff we can promise, and no way to resume a deeper crawl — if a `limit=500` call comes back with 180 posts, 180 posts is what is publicly reachable for that handle.

Why is views null on some Instagram posts?

Because Instagram only publishes a play count for video content. Still images and carousel posts have no public view figure, so `views` is null on them rather than zero. Likes can also be null when the creator has hidden the like count on that specific post. Treat null as absent data, not as an engagement of zero.

Does this return Instagram reels as well as photos?

Reels can appear in the grid results and are labelled through the `type` field, but this endpoint is scoped to the main profile feed. If reels are what you are analysing, use the Instagram Channel Reels API — it targets them directly and gives cleaner coverage than filtering these results after the fact.

Can I get posts from a private Instagram account?

No. We read only what a logged-out browser can see, so a private account returns an error and no charge. There is no setting, plan or parameter that changes this. If you are running a list of handles, check the account's `is_private` flag with the Instagram Channel Stats API first and skip the private ones.

Is the caption's hashtag list reliable?

Not entirely. The `hashtags` array comes from the upstream page and is sometimes null even on captions that clearly contain tags. If hashtag analysis is central to your project, parse `caption` yourself with a simple regex and use `hashtags` only as a fallback. The caption text itself is the more dependable field.

Related

GET /v1/instagram/statsGET /v1/instagram/taggedGET /v1/instagram/keyword-searchGET /v1/instagram/mentions The whole sheet