Skip to content

Python guide

Scrape Instagram posts
with Python.
One script. No Instagram login.

An Instagram scraper in one Python script: captions, likes, comments, plays and dates for a public account’s recent posts and reels, saved to CSV. One API key. No Instagram login, cookies or browser.

  • 1 creditper page
  • 12posts on the recorded page
  • 0Instagram logins
import csv
import os
import requests

API_URL = "https://www.monocrawl.com/v1/instagram/posts"
HEADERS = {"x-api-key": os.environ["MONOCRAWL_API_KEY"]}


def get_posts(handle, max_pages=10):
    params = {"handle": handle}
    for _ in range(max_pages):
        body = requests.get(API_URL, params=params, headers=HEADERS, timeout=60).json()
        if not body.get("success"):
            raise RuntimeError(body["error"]["message"])
        data = body["data"]
        yield from data["items"]
        if not data.get("has_more") or not data.get("cursor"):
            break
        params = {"handle": handle, "cursor": data["cursor"]}


with open("posts.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    writer.writerow(["code", "created_at", "product_type", "likes", "comments", "plays", "url", "caption"])
    for post in get_posts("gymshark"):
        stats = post.get("stats") or {}
        writer.writerow([post["code"], post["created_at"], post["product_type"], stats.get("likes"),
                         stats.get("comments"), stats.get("plays"), post["url"], post["caption"]])

$ python posts.py

GET /v1/instagram/posts handle=gymshark · page 1

✓ 12 posts · 1 credit · has_more: true

✓ page 2: 12 more posts · 1 credit · 0 repeats

↻ following the cursor until has_more is false or max_pages

✓ posts.csv · up to 10 pages ≈ 120 posts for 10 credits (estimate)

Replay of a real call, recorded 26 Sep 2026

Four steps

Copy, paste, run.
See what each step returns.

Terminal
export MONOCRAWL_API_KEY="mn_your_key_here"
pip install requests

What you get

MONOCRAWL_API_KEY=mn_••••••••Free plan · 1,000 credits a month · No card

What comes back

One page.
Real posts.

A real page of Gymshark’s Instagram posts: 12 posts and reels for 1 credit. 7 are below, with their likes, comments and, for reels, plays.

  • codeThe post’s shortcode, as in its URL
  • captionThe post’s full caption
  • created_atWhen, as an ISO timestamp
  • statsLikes, comments and, for reels, plays
  • product_typeclips for a reel, carousel_container for a carousel
  • urlThe post on instagram.com
  • media_urls, carouselIts images and video, and each slide
  1. The SulekMarket giveaway 😮‍💨

    2,79325161,085Reel26 Sept 2026
  2. The Gymshark x Sam Sulek collection is dropping soon. Get early access at Olympia weekend 👀 More info on stories.

    150,0294102,614,342Reel25 Sept 2026
  3. We’re hybrid athletes, we live by ‘AND’.

    5,54770484,606Reel20 Sept 2026
  4. Celebrando a nuestros atletas Hispanos 💪 ~ Celebrating our Hispanic athletes 💪

    44,300239Carousel17 Sept 2026
  5. More miles. More progress.

    23,584105Carousel16 Sept 2026
  6. Taking a moment to celebrate our strong women and everything they’ve worked for.

    66,685497Carousel15 Sept 2026
  7. WE LIVE BY ‘AND’.

    39,531215Carousel13 Sept 2026
Show the JSON
{
  "success": true,
  "data": {
    "has_more": true,
    "cursor": "mlc1.…",
    "items": [
      {
        "id": "3994978398578617970",
        "code": "DdxA6_soy5y",
        "url": "https://www.instagram.com/p/DdxA6_soy5y/",
        "caption": "The SulekMarket giveaway 😮‍💨",
        "created_at": "2026-09-26T21:37:03.000Z",
        "is_video": true,
        "product_type": "clips",
        "stats": {
          "likes": 2793,
          "comments": 25,
          "plays": 161085
        }
      },
      {
        "id": "3994049637738654738",
        "code": "DdttvvwsKwS",
        "url": "https://www.instagram.com/p/DdttvvwsKwS/",
        "caption": "The Gymshark x Sam Sulek collection is dropping soon.\nGet early access at Olympia weekend 👀\nMore info on stories.",
        "created_at": "2026-09-25T14:59:43.000Z",
        "is_video": true,
        "product_type": "clips",
        "stats": {
          "likes": 150029,
          "comments": 410,
          "plays": 2614342
        }
      },
      {
        "id": "3990521760637743999",
        "code": "DdhLmYLR3N_",
        "url": "https://www.instagram.com/p/DdhLmYLR3N_/",
        "caption": "We’re hybrid athletes, we live by ‘AND’.",
        "created_at": "2026-09-20T18:05:31.000Z",
        "is_video": true,
        "product_type": "clips",
        "stats": {
          "likes": 5547,
          "comments": 70,
          "plays": 484606
        }
      },
      {
        "id": "3988348163014063095",
        "code": "DdZdYXpCPP3",
        "url": "https://www.instagram.com/p/DdZdYXpCPP3/",
        "caption": "Celebrando a nuestros atletas Hispanos 💪\n~\nCelebrating our Hispanic athletes 💪",
        "created_at": "2026-09-17T18:02:52.000Z",
        "is_video": null,
        "product_type": "carousel_container",
        "stats": {
          "likes": 44300,
          "comments": 239,
          "plays": null
        }
      },
      {
        "id": "3987628542065640275",
        "code": "DdW5wgaCANT",
        "url": "https://www.instagram.com/p/DdW5wgaCANT/",
        "caption": "More miles. More progress.",
        "created_at": "2026-09-16T18:13:07.000Z",
        "is_video": null,
        "product_type": "carousel_container",
        "stats": {
          "likes": 23584,
          "comments": 105,
          "plays": null
        }
      },
      {
        "id": "3986896843400831908",
        "code": "DdUTY47CGOk",
        "url": "https://www.instagram.com/p/DdUTY47CGOk/",
        "caption": "Taking a moment to celebrate our strong women and everything they’ve worked for.",
        "created_at": "2026-09-15T17:59:22.000Z",
        "is_video": null,
        "product_type": "carousel_container",
        "stats": {
          "likes": 66685,
          "comments": 497,
          "plays": null
        }
      },
      {
        "id": "3985463540202382828",
        "code": "DdPNflTCKXs",
        "url": "https://www.instagram.com/p/DdPNflTCKXs/",
        "caption": "WE LIVE BY ‘AND’.\n\nThe Conditioning Club community came together to run, lift and recover.\nBeing a hybrid athlete means there’s more than one way to move.\n\nFor those who lift and run and refuse to choose.\nGymshark Conditioning Club returns soon.",
        "created_at": "2026-09-13T18:31:38.000Z",
        "is_video": null,
        "product_type": "carousel_container",
        "stats": {
          "likes": 39531,
          "comments": 215,
          "plays": null
        }
      }
    ]
  }
}

Cost

1 credit a page.
Nothing for failures.

Every page of posts costs 1 credit; both recorded pages returned 12. A profile costs 1 credit too. Slide to see what a collection costs.

10credits, 1 per page
100collections like this a month on the free plan
1,000on Starter, $29 a month

Estimated at 12 posts a page, as on both recorded pages. Failed calls are free.

Two more things

The profile,
errors and rate limits.

The account’s profile

For the account itself, call instagram/profile with the same handle: followers, following, post count, bio and verified flag, 1 credit. Recorded for Gymshark: 8,668,790 followers and 1,747 posts.

profile.py
PROFILE_URL = "https://www.monocrawl.com/v1/instagram/profile"

body = requests.get(PROFILE_URL, params={"handle": "gymshark"}, headers=HEADERS, timeout=60).json()
profile = body["data"]
print(profile["followers"], profile["following"], profile["posts_count"], profile["bio"])

Errors and rate limits

A failed call returns success: false with an error type and message, and costs nothing. On HTTP 429, wait and try again.

Rate limits by plan
posts.py
import time

def get_page(params, attempts=5):
    for attempt in range(attempts):
        response = requests.get(API_URL, params=params, headers=HEADERS, timeout=60)
        if response.status_code != 429:
            return response.json()
        time.sleep(2 ** attempt)
    raise RuntimeError("Still rate limited; try again later")

Other platforms

Same loop.
Swap the endpoint.

The same key collects an account’s posts on other platforms. Each endpoint’s reference lists its fields and paging.

Questions

Scraping Instagram posts,
answered.

Do I need an Instagram account, cookies or a browser?

No. You call Monocrawl with your API key and get the posts back as JSON. It works for public accounts.

How many posts does one call return?

One page. Both recorded pages returned 12 posts, with has_more set to true and a cursor for the next page. Pass the cursor back with the same handle.

What does it cost?

1 credit for each page of posts that comes back, and 1 credit for a profile. The free plan’s 1,000 credits a month cover about 1,000 pages, and failed calls are free.

Does it include reels and carousels?

Yes. Reels come back with product_type clips and a play count; carousels come back as carousel_container. The recorded page had 5 reels and 7 carousels. For reels only, use instagram/reels.

Can I get the comments on each post?

Yes. Pass each post’s url to instagram/post-comments, 3 credits a page. Our Instagram comments guide has the full loop.

Does Instagram have an official API?

Meta’s Instagram API works with professional accounts that connect to your app, plus basic data on other professional accounts. Our Instagram data API comparison sets out the official routes and the main providers side by side.

Start free

Posts from any public Instagram account.
1 credit a page.

1,000 free credits every month. No card required.

Get a free API key