Instagram Reels API — Reels With Play Counts

List an account’s reels

The reels a public Instagram account has published, newest first, each with its play, like and comment counts — the cheapest way to see what an account’s videos reach without a detail call per reel. Pass a username or profile URL. Paged: 12 reels per page, 1 credit per page fetched.

POST /user-reels Accounts
https://api.fetchlayer.dev/instagram/user-reels
OpenAPI JSON

Headers

Authorization string required

Bearer token — your FetchLayer API key.

Parameters

username string optional

A username with or without the @, or a profile URL. Give this or userId.

userId string optional

The account’s numeric id (profile.id), as a string of digits. Give this or username. An id names the account even after it changes its username; an id that belongs to no account is a 404. Name the account with exactly one of username and userId; both or neither is a 400.

include string[] optional

Optional extra counts: any of "reposts", "shares" and "saves". "reposts" fills repostCount on every kind of post; "shares" and "saves" fill shareCount and saveCount on reels and videos (photos and carousels have none). It fills them on every post of the page and adds a few seconds per page, usually 2 to 8. A count you asked for is never silently empty: when it is not available for some posts, the note names them — "The repost, share and save counts asked for are not available for these posts in this response, so they are empty: <short codes>. Asking again, or post-detail with include, returns them." The price is unchanged: 1 credit per page fetched. Without include the three are null and the note says so. "native" adds each reel’s native record (below), about a second more per 24 reels, at the same price. "taggedUsers" (with "native") adds the tagged accounts to reels that have them, in taggedUsers and native.usertags: about 1 to 2 seconds more on a page with tagged reels. A reel whose tags could not be read in time names "taggedUsers" in its missingFields. Any other value is a 400; "country" belongs to user-profile.

limit integer optional

Maximum items returned, up to 240. Use -1 for the maximum. Without it, every item on the pages fetched comes back, 12 a page (pages: 2 alone returns 24). A limit takes as many pages as it needs: a limit of 60 takes five pages and costs five credits. (default: every item on the pages fetched, 12 a page)

pages integer optional

Pages of results to return in one request, up to 20. Use -1 for the maximum. Instagram serves 12 items per page and each page fetched is 1 credit, so a call costs the pages it fetches — set by pages, or by how many pages a limit needs — not the number of items returned. The response reports them as pagesFetched. (default: 1)

cursor string optional

Continue from a previous response’s nextCursor. Send it back to the same route, with the same account, that produced it; a cursor from another route or another account, or a malformed one, is a 400.

format string optional

json or markdown, lowercase; any other value is a 400. Over MCP, markdown returns the result rendered as text; the REST API accepts it and returns JSON. (default: json)

timeoutMs integer optional

Time limit for the whole request, in milliseconds (1000–120000); a value outside that range is a 400. When it runs out, a paged request returns what it has with nextCursor, billed for the pages it returned; a request with nothing to return yet fails with a 503 you can retry, marked with the response header X-Timeout-Source: caller. Left out, the request runs as long as it needs.

Response Fields

user-reels

List an account’s reels

transport string

The data source: always the platform id, "instagram".

notes string[]

Caveats you can act on — a ceiling Instagram imposes, a counter it withholds, a list that stopped early. Several real limits are reported only here, so read them. An empty result always comes with a note explaining it, never as an unexplained empty array.

pagesFetched number

Pages of results fetched, which is what the request costs: one credit per page. resolve-url reports 0 and is not billed.

restricted boolean

True for an account Instagram restricts — typically an age-restricted brand such as alcohol or cannabis — and false for every other. Such an account answers 200, not 404, with less detail. It returns the account’s video posts.

minimumAge integer | null

The minimum age Instagram states for a restricted account, or null when it states none (and on every other account).

username string

The account the reels belong to.

posts object[]

The reels, newest first — each with playCount, likeCount and commentCount filled in. Each normally also carries its date, videoDuration, caption and video links (fieldsFrom "listing_full"), and with include every reel does. A reel still missing one of them is named in the note by short code, with the fields it lacks; asking again with include returns them, and so does post-detail. Empty for a private account, with the note "This account is private, so its posts and reels are not public." When timeoutMs runs short, only the leading reels that are complete come back — possibly none, as an empty list that is still a 200 — with status partial, partialReasons [time_limit], stopReason time_limit and a nextCursor at the first reel not yet complete, so the next call returns it complete and nothing is skipped or repeated. The one exception: a reel deleted between calls can make one reel repeat or be passed over.

posts[].id string

Instagram’s exact media id, as a string: it is a 64-bit number, larger than a JSON number can carry exactly, so keep it as a string. post-detail and post-comments accept it as post.

posts[].shortcode string

The code in the post URL. post-detail and post-comments both accept it.

posts[].url string

Canonical link to the post or reel.

posts[].kind string

image, video or carousel.

posts[].productType string | null

What Instagram calls the surface — "feed" for a grid post, "clips" for a reel — or null.

posts[].caption string | null

The author’s own written post body, or null. This is a caption, not a transcript: Instagram publishes no transcript of spoken audio.

posts[].createdAt string | null

ISO 8601 timestamp, or null: always null on reels-feed, and null on a listing record whose fieldsFrom is "listing".

posts[].likeCount integer | null

Likes, or null: always null on reels-feed, null on a listing record whose fieldsFrom is "listing", and absent when countsHidden is true.

posts[].commentCount integer | null

Comments, or null in the same cases as likeCount.

posts[].playCount integer | null

Instagram plays on a video or reel: the view count Instagram shows on it, the same figure on listings and on post-detail (plays on Facebook are not included). Null for photos and carousels, whose views Instagram does not make public, and for posts from a restricted account. On post-detail it can occasionally be null for a video; the note says so, and a retry usually returns it.

posts[].videoDuration number | null

Length of a video or reel in seconds, with decimals. Null for a photo or carousel (each carousel frame carries its own carousel[].videoDuration), on reels-feed, and on a listing record whose fieldsFrom is "listing".

posts[].shareCount integer | null

How many times the post was shared, as Instagram counts it under a reel. Filled when include lists "shares" (post-detail, user-posts, user-reels); reels and videos only, null on photos and carousels. Also null — not 0 — when the account hides it, and the note says so.

posts[].repostCount integer | null

How many times the post was reposted to other accounts’ profiles. Filled when include lists "reposts" (post-detail, user-posts, user-reels); every kind of post.

posts[].saveCount integer | null

How many times the post was saved. Filled when include lists "saves" (post-detail, user-posts, user-reels); reels and videos only, null on photos and carousels.

posts[].countsHidden boolean | null

True when the account has turned like and view counts off. The counts are then absent rather than zero.

posts[].width integer | null

Pixel width, or null where the record does not carry it.

posts[].height integer | null

Pixel height, or null where the record does not carry it.

posts[].altText string | null

Instagram’s own auto-generated image description ("Photo by NASA on August 19, 2026. May be an image of text."), or null. Machine-written alt text, not a caption and not a transcript.

posts[].thumbnailUrl string | null

Preview frame, or null.

posts[].assets object[]

Ordered and de-duplicated renditions, each with kind, url, width, height and downloadUrl.

posts[].assets[].downloadUrl string | null

Pass it to GET /media to stream the bytes. Instagram’s own links are signed and expire; this one keeps working.

posts[].carousel object[]

One entry per frame of a carousel, each with its own renditions and, for a video frame, its own videoDuration. Empty for a single image or video.

posts[].author object

id, username, fullName, profilePicUrl, verified, privateAccount and profileUrl. Fields Instagram does not publish on a listing are null.

posts[].coauthors object[]

Accounts credited as co-authors of a collab post.

posts[].taggedUsers object[]

Accounts tagged in the image.

posts[].location object | null

The place the post was tagged at, or null.

posts[].hashtags string[]

Without the #.

posts[].mentions string[]

Usernames without the @.

posts[].topics string[]

Instagram’s own subject labels for the post ("jwst", "star birth"), where it publishes them.

posts[].hasAudio boolean | null

Whether the video carries audio, or null.

posts[].fieldsFrom string

Which shape this record has, so a null count is never mistaken for a zero. "detail" is a full post, from post-detail. "listing_full" is a listing record with its posting date and counts, which is what user-posts and user-reels return. "listing" is a record without them: every reel on reels-feed, and on user-posts or user-reels only a post that is named in the note by short code, with the fields it lacks. Asking again with include returns them, and so does post-detail.

posts[].audio object | null

The sound on a video or reel, where Instagram publishes it with the post; null otherwise. Ids are exact strings.

posts[].audio.type string | null

Instagram’s label for the sound, e.g. original_sounds or licensed_music.

posts[].audio.originalSoundTitle string | null

The title of an original sound, e.g. Original audio.

posts[].audio.artistUsername string | null

The account an original sound is credited to.

posts[].audio.durationMs integer | null

Length of the sound in milliseconds.

posts[].audio.audioAssetId string | null

The sound’s own id.

posts[].audio.canonicalId string | null

The id Instagram groups uses of the same sound under.

posts[].audio.songTitle string | null

Licensed music: the song’s title. Null for an original sound, or a song Instagram no longer makes available.

posts[].audio.songArtist string | null

Licensed music: the artist as Instagram shows it.

posts[].audio.isExplicit boolean | null

Licensed music: whether Instagram marks the song explicit. Null when there are no song details.

posts[].audio.audioClusterId string | null

Licensed music: the id Instagram groups every use of the song under.

posts[].fullId string | null

Instagram’s full id for the post, <id>_<author id>. Null when the author’s id is not known.

posts[].videoDownloadUrl string | null

The first video’s downloadUrl, for GET /media, valid as long as its own link. Null for a photo, or when this response has no video link for the post.

posts[].thumbnailDownloadUrl string | null

thumbnailUrl as a downloadUrl for GET /media, valid as long as thumbnailUrl is.

posts[].brandedContent object

The paid-partnership label, and how far the accounts a post names are known in this response. Each status is found (present), not-published (Instagram shows none) or not-looked-up (not known in this response — never a statement that there is none). Sponsor accounts are not returned.

posts[].brandedContent.isPaidPartnership boolean | null

Instagram’s paid-partnership label. Null when not known in this response.

posts[].brandedContent.paidPartnershipStatus string

found, not-published or not-looked-up, for isPaidPartnership.

posts[].brandedContent.coauthorsStatus string

found, not-published or not-looked-up, for coauthors.

posts[].brandedContent.taggedUsersStatus string

found, not-published or not-looked-up, for taggedUsers.

posts[].brandedContent.captionTagsStatus string

found, not-published or not-looked-up, for hashtags and mentions, which come from the caption and carry no account ids.

posts[].missingFields string[]

The fields this post still lacks in this response, empty when it has them all: any of createdAt, caption, likeCount, commentCount, playCount, videoDuration, width, height and videoUrl (no video link in assets), and on user-posts and user-reels taggedUsers when include asked for it and the tags could not be read in time. A field the post really has none of — no caption, a count the account turned off — is not listed. More values may be added.

posts[].isPinned boolean | null

True for a reel the account has pinned to the top of its reels, false otherwise, null when Instagram does not say. Pinned reels come first on page one and are never repeated on a later page.

posts[].native object | null

Only with include ["native"]: the reel’s full record as Instagram publishes it, field for field under Instagram’s own field names, with every id an exact string — among others the full id, code, posting time, original size, length, every rendition, the Instagram-only, total and Facebook play counts, like, comment, share, save and repost counts, co-authors, the paid-partnership label and the sound. Null when not available this time (nativeUnavailableReason says why); absent without the include.

posts[].nativeUnavailableReason object | null

Only with include ["native"]: null when native is present, otherwise {code, retryable} saying why it is null: time-limit (timeoutMs left too little time; allow more), temporarily-unavailable (asking again usually returns it), or capacity (full records are temporarily unavailable for this request; retry shortly). All are retryable, and the rest of the response is unaffected.

status string

What the answer amounts to. ok: what was asked for (a continuation past the last page is ok with no posts). partial: something is missing; partialReasons says what. empty: the account has nothing in this list. private: the account is private. restricted: Instagram restricts the account. More values may be added; treat one you do not recognise as partial.

partialReasons string[]

Why the answer is partial; empty otherwise. time_limit: timeoutMs ran out; continue with nextCursor. stopped_early: a later page was not returned; continue with nextCursor. fields_missing: some posts lack a field, named in each post’s missingFields. counts_unavailable: some counts asked for with include are empty. More values may be added.

complete boolean

True when every page asked for was read and every reel returned is complete (its missingFields is empty).

stopReason string

Why the list stopped where it did. exhausted: nothing after it (hasMore false). page_limit: the limit or pages asked for were reached. time_limit: timeoutMs ran out. upstream_stopped: a later page was not returned. Continue with nextCursor for every value but exhausted. More values may be added.

postCount number

How many items are in this response.

hasMore boolean

Whether more results exist beyond what was returned.

nextCursor string | null

Opaque value to pass back as cursor, to the same route (and the same account on an account listing), to continue. null when there is nothing further. It can start part-way through a page, at a named reel; treat it as opaque.

Request

curl -X POST "https://api.fetchlayer.dev/instagram/user-reels" \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"username":"@nasa","limit":12,"include":["reposts","shares","saves"]}'

Response

{
  "transport": "instagram",
  "notes": [
    "More results are available. Continue with nextCursor."
  ],
  "pagesFetched": 1,
  "posts": [
    {
      "id": "3986188199829823669",
      "shortcode": "DdRyQxKteC1",
      "url": "https://www.instagram.com/reel/DdRyQxKteC1/",
      "kind": "video",
      "productType": "clips",
      "caption": "Sunrise, sunset, sunrise again. Astronauts aboard the @ISS see 16 of each every day. 🌅",
      "createdAt": "2026-09-15T15:02:11.000Z",
      "likeCount": 135117,
      "commentCount": 1667,
      "playCount": 6142932,
      "videoDuration": 31.4,
      "shareCount": 10242,
      "repostCount": 6685,
      "saveCount": 7889,
      "countsHidden": false,
      "width": 1080,
      "height": 1920,
      "altText": null,
      "thumbnailUrl": "https://scontent-ham3-1.cdninstagram.com/v/t51.82787-15/...",
      "assets": [
        {
          "kind": "video",
          "url": "https://scontent-ham3-1.cdninstagram.com/o1/v/t2/f2/m86/...",
          "width": 720,
          "height": 1280,
          "downloadUrl": "https://api.fetchlayer.dev/instagram/media?url=..."
        }
      ],
      "carousel": [],
      "author": {
        "id": "528817151",
        "username": "nasa",
        "fullName": "NASA",
        "profilePicUrl": "https://scontent-ham3-1.cdninstagram.com/v/t51.2885-19/...",
        "verified": true,
        "privateAccount": false,
        "profileUrl": "https://www.instagram.com/nasa/"
      },
      "coauthors": [],
      "taggedUsers": [],
      "location": null,
      "hashtags": [],
      "mentions": [
        "iss"
      ],
      "topics": [],
      "hasAudio": true,
      "fieldsFrom": "listing_full"
    }
  ],
  "postCount": 12,
  "hasMore": true,
  "nextCursor": "AQHT6ML6wCLtibE4BLFfMtyM6RwkyO",
  "restricted": false,
  "minimumAge": null,
  "username": "nasa"
}
Product overviewInstagram API: what it returns, pricing and use cases