# Instagram API in Python: profiles, posts and comments (2026)

> What Instagram's official API covers, and a Python script that reads any public account's profile, posts, comments and reel transcripts with requests: parameters, paging and cost.

Canonical page: https://okdata.online/guides/instagram-api-python/

Instagram's official API serves professional accounts you own or manage. For public data on any account, four endpoints in this order: Instagram account profile, Instagram account posts, Instagram post comments and Instagram reel transcript. The worked example is 8 calls and costs at most $0.032; a failed call is not charged.

Updated 2026-11-12

Instagram does have an official API, and it is the right tool for one kind of job. Meta's [Instagram Platform overview](https://developers.facebook.com/docs/instagram-platform/overview/) describes it as a set of APIs for Instagram professional accounts, meaning business and creator accounts, that you own or manage, or whose owners log in to your app. It covers publishing, comment moderation, messaging and insights. For other people's accounts it offers [Business Discovery](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/business-discovery/), which returns basic metadata and metrics about other professional accounts, and nothing for age-gated ones. If your job is your own account, use Meta's API.

This guide is for the other job: reading public data on any public account, a competitor, a creator you might hire, a brand you study, from Python with nothing but `requests`. You end with one JSON file per account: the profile, the recent posts with their counts, the comments on the posts that drew the most discussion, and what is said in the most played reels, with timestamps.

No Instagram login, Meta app or access token is involved. Each call is an HTTPS request with your key.

## Step by step

### Step 1: the profile

[Instagram account profile](https://okdata.online/apis/instagram/users/get/) takes `username`, with or without @, and returns one account for $0.004: `id` (the numeric account ID), `username`, `name`, `bio`, `followers`, `following`, `posts`, `verified` and `avatar_url`. With `view=full` you also get `ext.private`, `ext.website`, `ext.links` (the links in the bio), `ext.category` and `ext.business`. `likes` is null: Instagram shows no account-wide like total.

```bash
curl -G "https://api.okdata.online/v1/instagram/users/get" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  -d username=northfieldbakery -d view=full
```

Check `ext.private` before going further. A private account's profile comes back, but its posts are not public.

### Step 2: the posts

[Instagram account posts](https://okdata.online/apis/instagram/users/posts/list/) takes `username` and `type`: `all` (the default, every post) or `reel` (reels only, from the account's reels tab). Posts come newest first, 12 a page, for $0.004 a page. Each post has:

- `id`: the shortcode, the part after `/p/` or `/reel/` in a link, such as `DQx7pLk2mVe`. The comment and transcript endpoints take it.
- `type`: `video` for reels and videos, `image` for photos and carousels.
- `text` (the caption), `hashtags`, `images`, `published_at` and `url`.
- `metrics.likes`, `metrics.comments` and, for videos, `metrics.views` (plays).

With `view=full`, `ext.duration_s`, `ext.coauthors` (the other authors of a collab post), `ext.product_type` and `ext.media_id` are added. Send `page.next_cursor` back as `cursor` for the next page and stop when `page.has_more` is `false`.

```bash
curl -G "https://api.okdata.online/v1/instagram/users/posts/list" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  -d username=northfieldbakery -d type=all -d view=full
```

### Step 3: the comments

[Instagram post comments](https://okdata.online/apis/instagram/posts/comments/list/) takes the shortcode as `id`, or the post's link as `url`. Comments come newest first, about 15 a page, for $0.004 a page. Each has `id`, `text`, `author.username`, `likes`, `replies` (how many replies sit under it) and `published_at`. `author.name` is null. To read the replies under one comment, pass its `id` and the post's shortcode as `post_id` to [Instagram comment replies](https://okdata.online/apis/instagram/comments/replies/list/).

```bash
curl -G "https://api.okdata.online/v1/instagram/posts/comments/list" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  -d id=DQx7pLk2mVe
```

### Step 4: what a reel says

[Instagram reel transcript](https://okdata.online/apis/instagram/posts/transcript/get/) takes a shortcode or link and returns what is said in a reel or video post, for $0.004: `segments` with `start` and `end` in seconds, and the whole `text`. It handles videos up to two minutes long. `lang` comes back as `und` (not identified), so detect the language yourself if it matters. A video with no speech returns `not_found`, which is not charged.

```bash
curl -G "https://api.okdata.online/v1/instagram/posts/transcript/get" \
  -H "Authorization: Bearer $OKDATA_API_KEY" \
  -d id=DRb3nVw8kQz
```

### Step 5: the whole script

The script makes at most eight calls: one profile, up to three post pages, two comment pages and two transcripts. Each logical request gets its own `Idempotency-Key`, so a retry after a dropped connection is never billed twice. `rate_limited` waits for `retry_after_seconds`; any other error stops with the code and message.

```python
import json
import os
import time
import uuid
import requests

API = "https://api.okdata.online"
KEY = os.environ["OKDATA_API_KEY"]
USERNAME = "northfieldbakery"
POST_PAGES = 3      # 12 posts a page; each page is one call
COMMENT_POSTS = 2   # first comment page of the most commented posts
TRANSCRIPTS = 2     # the most played videos of two minutes or less


class ApiError(Exception):
    def __init__(self, body):
        super().__init__(f"{body['code']}: {body['message']} (charged: {body['charged']})")
        self.code = body["code"]


def call(path, **params):
    headers = {"Authorization": f"Bearer {KEY}", "Idempotency-Key": str(uuid.uuid4())}
    for attempt in range(3):
        try:
            body = requests.get(API + path, headers=headers, params=params, timeout=90).json()
        except requests.RequestException:
            time.sleep(2 ** attempt)  # same key on the retry: no double charge
            continue
        if body["code"] == "ok":
            return body
        if body["code"] == "rate_limited":
            time.sleep(body.get("retry_after_seconds") or 2)
            continue
        raise ApiError(body)
    raise RuntimeError(f"{path}: no answer after 3 attempts")


profile = call("/v1/instagram/users/get", username=USERNAME, view="full")["data"]["item"]
if profile["ext"]["private"]:
    raise SystemExit(f"@{USERNAME} is private; its posts are not public")

posts, cursor = [], None
for _ in range(POST_PAGES):
    body = call("/v1/instagram/users/posts/list", username=USERNAME, view="full", **({"cursor": cursor} if cursor else {}))
    posts += body["data"]["items"]
    if not body["page"]["has_more"]:
        break
    cursor = body["page"]["next_cursor"]

for post in sorted(posts, key=lambda p: p["metrics"]["comments"] or 0, reverse=True)[:COMMENT_POSTS]:
    post["comment_page"] = call("/v1/instagram/posts/comments/list", id=post["id"])["data"]["items"]

videos = [p for p in posts if p["type"] == "video" and (p["ext"]["duration_s"] or 0) <= 120]
for post in sorted(videos, key=lambda p: p["metrics"]["views"] or 0, reverse=True)[:TRANSCRIPTS]:
    try:
        post["transcript"] = call("/v1/instagram/posts/transcript/get", id=post["id"])["data"]["item"]
    except ApiError as err:
        if err.code != "not_found":  # no speech in the video: not charged
            raise

with open(f"{USERNAME}.json", "w", encoding="utf-8") as f:
    json.dump({"profile": profile, "posts": posts}, f, ensure_ascii=False, indent=2)
print(profile["followers"], "followers;", len(posts), "posts saved")
```

Raise `POST_PAGES` to go further back: ten pages is about 120 posts and costs $0.04 for the posts alone.

## Good to know

- **Shortcodes are the post IDs.** Every post endpoint takes the shortcode as `id`, or the full link as `url`. `ext.media_id` holds Instagram's numeric media ID if another tool needs it.
- **Comments are newest first.** There is no "most liked" order. To find the comments that resonated, read a few pages and sort by `likes` yourself.
- **What is null.** Posts have no `title`, and shares, saves and reposts are null because Instagram does not show them. Plays exist only for videos.
- **Reels only.** `type=reel` lists the reels tab. When video is what you study, it saves the pages you would otherwise spend on photos.
- **Failed calls cost nothing**, and so do `not_found` answers for deleted posts and videos without speech.
- **Other accounts on the official API.** Business Discovery covers basic metadata and metrics of other professional accounts. When that is enough and you already run a Meta app, it is a fine option.

To weigh Meta's API against a third-party API for your project, see [official API vs third-party social data](https://okdata.online/answers/official-api-vs-third-party-social-data/). Watching a few accounts over time is covered in [competitor monitoring](https://okdata.online/solutions/competitor-monitoring/).

## What the example costs

One account's profile, its last three pages of posts, the first comment page of its two most commented posts and the transcripts of its two most played reels

| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| [Instagram account profile](https://okdata.online/apis/instagram/users/get/) | 1 | $0.004 | $0.004 |
| [Instagram account posts](https://okdata.online/apis/instagram/users/posts/list/) | 3 | $0.004 | $0.012 |
| [Instagram post comments](https://okdata.online/apis/instagram/posts/comments/list/) | 2 | $0.004 | $0.008 |
| [Instagram reel transcript](https://okdata.online/apis/instagram/posts/transcript/get/) | 2 | $0.004 | $0.008 |
| Total | 8 |  | $0.032 |

Prices in force when this page was built. A failed call costs nothing.

## In your AI (MCP)

Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.

Say: Look up the Instagram account northfieldbakery: followers and bio, its last 36 posts with likes, comments and plays, the first page of comments on its two most commented posts, and what is said in its two most played reels. Summarise what its audience asks about most.

```json
{
  "endpoint": "instagram.users.get",
  "params": {
    "username": "nasa"
  },
  "idempotency_key": "<a new UUID>"
}
```

MCP: `https://api.okdata.online/mcp`

## Endpoints in this guide

- [Instagram account profile](https://okdata.online/apis/instagram/users/get/) `instagram.users.get`: $0.004 per call
- [Instagram account posts](https://okdata.online/apis/instagram/users/posts/list/) `instagram.users.posts.list`: $0.004 per call · about 12 posts per page
- [Instagram post comments](https://okdata.online/apis/instagram/posts/comments/list/) `instagram.posts.comments.list`: $0.004 per call · about 15 comments per page
- [Instagram reel transcript](https://okdata.online/apis/instagram/posts/transcript/get/) `instagram.posts.transcript.get`: $0.004 per call

## More about Instagram

- [Instagram API](https://okdata.online/apis/instagram/)
- [Comments API](https://okdata.online/roles/posts-comments-list/)
- [Profile API](https://okdata.online/roles/accounts-get/)
- [Account posts API](https://okdata.online/roles/accounts-posts-list/)
- [Transcript API](https://okdata.online/roles/transcripts-get/)

## Sources

- [Instagram Platform overview](https://developers.facebook.com/docs/instagram-platform/overview/) (read 2026-11-12)
- [Instagram API with Facebook Login: Business Discovery](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-facebook-login/business-discovery/) (read 2026-11-12)
