InstagramAnswer

Instagram reels API: publish your own, list any public account's

Meta's API publishes reels and reads your own; other accounts only via Business Discovery. Ours lists a public account's reels with plays, 12 a call.

8 min read

Short answer

  • Instagram's API has reels support for professional accounts: you publish a reel with media_type=REELS and read your own reels and their metrics. Meta's publishing guide caps an account at 100 API-published posts in 24 hours in one place and 50 in another (Meta's pages, read 2026-10-17).
  • Another account's reels come only through Business Discovery, for Business and Creator targets. Meta lists no reels edge, and media_url is left out when a reel's owner has turned downloads off.
  • ToolzerHub's /v1/instagram/user/reels lists a public account's reels with play counts, 12 per call at 1 credit. On 2026-10-17 it paged MrBeast to the end: 705 reels in 59 calls, back to December 2022. Unknown usernames return a 503, not an empty result.
On this page

Instagram's API publishes reels and reads your own, from a professional account you log in as. Other accounts' reels come only through Business Discovery, for Business and Creator accounts. ToolzerHub lists any public account's reels by username, 12 per call with play counts but no watch-time metrics; an unknown username returns a 503.

Does Instagram have a reels API?

Yes, as part of the Instagram Platform. Meta's content publishing guide (updated June 30, 2026) shows "how to publish single images, videos, reels (single media posts), or posts containing multiple images and videos (carousel posts) on Instagram professional accounts". There is no separate reels product. A reel is a media object, and Meta's IG Media reference describes media as "an Instagram album, photo, or video (uploaded video, live video, reel, or story)".

You want Meta's route Who
Publish a reel media_type=REELS Your account
Your reels media edge Your account
Watch time, reach media insights Your account
Others' reels Business Discovery Business or Creator
A reels list none n/a

Publishing reels

"To publish a reel, create a container for the video and include the media_type=REELS parameter along with the path to the video using the video_url parameter." Then you publish the container. Reels can't go in carousels, and a published reel reads back as VIDEO: Meta says to "request its media_product_type field instead" to tell it apart.

The daily limit depends on where you read it. The guide's Rate Limit section says "Instagram accounts are limited to 100 API-published posts within a 24-hour moving period", and its carousel section on the same page says "Accounts are limited to 50 published posts within a 24-hour period." The content_publishing_limit edge shows an account's current usage.

Reading reels from other accounts

On your own reels, media insights add watch time, reach and skip rate. For anyone else's, Business Discovery is the route, from an app with Facebook Login and only for Business or Creator targets. Its sample returns view_count for a reel, which Meta describes as "View count for Instagram Reels, which includes both paid and organic metrics", and marks "Available for Business Discovery API only."

The video file is not guaranteed. Meta leaves out media_url for video with copyrighted or licensed audio, and when "The reel owner has turned off downloads for the reel", a rule that applies "only on endpoints that read other users' media, such as business discovery". Meta's advice is to "Treat media_url as an optional field". The IG User reference lists no reels edge, so there is no call that returns an account's Reels tab. We did not call Meta's API; this is from its documentation.

List a public account's reels

We make ToolzerHub. Our Instagram API lists the reels on a public account's Reels tab by username, with one API key and no Meta app. GET /v1/instagram/user/reels returns 12 reels per call and costs 1 credit; pass paging_info.max_id back as cursor until more_available is false (our OpenAPI file, 2026-10-17). Each reel has code, taken_at, play_count, like_count, comment_count and its caption. play_count is the number shown on the Reels tab; we have not compared it with Meta's view_count.

The posts list doesn't replace it. MrBeast's posts list (/v1/instagram/user/posts, paged to the end on 2026-10-16) held 211 reels; his reels list held 705. Our post on getting a user's posts covers that list.

This script writes each account's reels to CSV.

python
import csv
import os
import sys
import time
from datetime import datetime, timezone

import requests

URL = "https://api.toolzerhub.com/v1/instagram/user/reels"
HEADERS = {"x-api-key": os.environ.get("TOOLZERHUB_API_KEY", "")}
FIELDS = ["account", "owner", "code", "url", "posted_at", "plays", "likes", "comments", "caption"]
calls = 0


def cell(value):
    # Spreadsheets run text starting with = + - or @ as a formula; an apostrophe keeps it text.
    if isinstance(value, str) and value[:1] in ("=", "+", "-", "@", "\t", "\r"):
        return "'" + value
    return value


def get(params, tries=4):
    # 429 and 503 are retried; the wait comes from the error body or the Retry-After header.
    global calls
    for attempt in range(tries):
        r = requests.get(URL, headers=HEADERS, params=params, timeout=90)
        calls += 1
        if r.status_code == 400:  # malformed username: skip this account
            print("bad request:", r.json()["error"]["message"].splitlines()[0])
            return None
        if r.status_code not in (429, 503) or attempt == tries - 1:
            if r.status_code >= 500:  # any 5xx, or a 503 past the retries: skip
                print(f"HTTP {r.status_code}, skipping:", r.text[:120].replace("\n", " "))
                return None
            r.raise_for_status()
            return r.json().get("data")
        try:
            wait = r.json()["error"]["details"]["retryAfter"]
        except (ValueError, KeyError, TypeError):
            wait = None
        try:
            wait = int(wait or r.headers.get("Retry-After") or 5 * (attempt + 1))
        except ValueError:
            wait = 5 * (attempt + 1)
        time.sleep(min(wait, 60))


def export_account(writer, account, max_pages):
    params, seen, pages = {"username_or_url": account}, set(), 0
    status = "page cap reached, more available"
    while pages < max_pages:
        data = get(params)
        if not data or not data.get("items"):
            status = "stopped early: failed or empty page" if pages else "nothing returned"
            break
        pages += 1
        for item in data["items"]:
            reel = item.get("media") or item
            key = reel.get("pk") or reel.get("code")
            if not key or key in seen:  # pages can repeat a reel
                continue
            seen.add(key)
            code = reel.get("code") or ""
            caption = (reel.get("caption") or {}).get("text") or ""
            writer.writerow({
                "account": account, "owner": (reel.get("user") or {}).get("username"),  # collabs differ
                "code": code, "url": f"https://www.instagram.com/reel/{code}/" if code else "",
                "posted_at": datetime.fromtimestamp(reel["taken_at"], timezone.utc).isoformat()
                if reel.get("taken_at") else "",
                "plays": reel.get("play_count"), "likes": reel.get("like_count"),
                "comments": reel.get("comment_count"),
                "caption": cell(caption.replace("\r", " ").replace("\n", " ")),
            })
        info = data.get("paging_info") or {}
        if not (info.get("more_available") and info.get("max_id")):
            status = "end"
            break
        params["cursor"] = info["max_id"]
        time.sleep(0.3)
    print(f"{account}: {len(seen)} reels, {pages} pages ({status})")
    return len(seen)


def main(accounts, max_pages="50", out="reels.csv"):
    total = 0
    with open(out, "w", newline="", encoding="utf-8-sig") as f:  # BOM: Excel reads emoji
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        for account in [a.strip().lstrip("@") for a in accounts.split(",") if a.strip()]:
            total += export_account(writer, account, int(max_pages))
    print(f"wrote {total} reels to {out} in {calls} calls")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit("usage: python instagram_reels.py spacex,nasa [max_pages] [file.csv]")
    if not HEADERS["x-api-key"]:
        sys.exit("set TOOLZERHUB_API_KEY first")
    main(*sys.argv[1:])

A 400 skips that account. A 429 or 503 is retried with the wait from retryAfter or Retry-After; a 503 that keeps coming back, or any other 5xx, skips the account, and a 429 that outlasts the retries, a 401 or a 402 stops the run with an error. The owner column shows who posted each reel, since collaborative reels from other accounts appear in the list. We tested those branches against a stand-in server, not live.

Run it

bash
export TOOLZERHUB_API_KEY=your_key
python instagram_reels.py "spacex, @bbcnews ,,nosuchuser9q8z7x" 30 reels.csv

Output on 2026-10-17, with a stray space, an @, an empty item and an unknown username added:

text
spacex: 78 reels, 7 pages (end)
bbcnews: 195 reels, 17 pages (end)
HTTP 503, skipping: {"error":{"code":"SITE_UNAVAILABLE","status":503,"message":"Upstream returned 502","retryable":true,"details":{"reason":
nosuchuser9q8z7x: 0 reels, 0 pages (nothing returned)
wrote 273 reels to reels.csv in 29 calls

It took 186 seconds, partly because the unknown username was tried four times: our route answers an unknown name with a retryable 503 instead of an empty result. SpaceX's reels had a median of about 2.4 million plays. One BBC News page of 12 came back without dates, captions or owner, so those cells are empty in the CSV.

What we measured

All numbers are from 2026-10-17. Each run went until more_available was false; BBC News is from an earlier script run, and its oldest date leaves out 12 undated rows.

Account Reels Calls Oldest
SpaceX 78 7 2022-02-03
BBC News 207 18 2022-12-14
NASA 292 25 2022-08-01
MrBeast 705 59 2022-12-23
  • The list is not fixed for every account. SpaceX gave 78 both times; BBC News gave 207, then 195 in the run above, with 171 reels in both.
  • The counts include collaborative reels posted by other accounts: 39 of BBC News's 195 came from 10 other accounts, and 11 of MrBeast's first 480 on 2026-10-16.
  • Every page held 12 reels except the last, with no repeats. count accepts up to 50, but Instagram serves 12.
  • The oldest reel matched the posts list. NASA's posts list, 2,000 posts back to August 2020, held no reel older than 2022-08-01; its older videos are typed feed or igtv. MrBeast's oldest reel in both lists is 2022-12-23.
  • detail=summary is lighter and faster (median 1.2 seconds a call against 3.2 on SpaceX) but has no date or caption.
  • Reliability: in the 7 days to 2026-10-17 the route had 136 successful calls and 1 server error, and 12 repeat calls on four accounts all returned 12 reels.

What it costs

One credit per call of 12 reels, at $0.99 to $1.90 per 1,000 credits ($1.45 on the $29 plan). MrBeast's 705 reels took 59 credits: 6 to 11 cents. 1,000 reels are about 84 calls, 8 to 16 cents. The free tier's 100 credits a month cover about 1,200 reels. Failed calls cost nothing.

Where Meta's API is the better pick

Use Meta's API to publish reels and to read your own reels' average watch time, skip rate and reach. It costs nothing per call and Meta supports it. Our route reads public accounts only, publishes nothing, and returns no watch-time metrics. Business Discovery's view_count counts paid views too; we don't know whether play_count does. For comments on a reel, see getting Instagram comments.

We are not affiliated with Meta Platforms, Inc. or Instagram.

Sources

Get this data from your own code

The Instagram API returns it as JSON with one x-api-key header. 100 free credits monthly.