Instagram Stories Scraper - Live Stories, Own Cookie avatar

Instagram Stories Scraper - Live Stories, Own Cookie

Pricing

Pay per event

Go to Apify Store
Instagram Stories Scraper - Live Stories, Own Cookie

Instagram Stories Scraper - Live Stories, Own Cookie

Twenty-four hours after it goes up a story is gone, so a run is only ever a snapshot of what is live right now. Media URLs, type, timestamp, mentions, links, hashtags and poster, from public profiles. No login, no cookie, no app review. $0.40 per 1,000 stories.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

1

Monthly active users

13 hours ago

Last modified

Share

Instagram Stories Scraper

Extract the currently available stories from public Instagram accounts. Runs work with no setup: no login, no cookie, no app review, nothing to supply.

A story is ephemeral and never indexed, so Instagram serves a live tray only to a signed-in caller — there is no logged-out route to it at any price. This actor reads the tray through a licensed data source, so you supply nothing.

What you get

  • Every story a public account has live at the moment of the run, image and video alike
  • Direct media URLs, media type, duration, and the posted timestamp
  • Caption, paid-partnership flag, and location when the story carries one
  • A canonical story URL, plus the owner's username and numeric profile id
  • Up to five accounts per run, up to 50 stories from each

It returns story metadata and direct media URLs. It does not download media binaries, read private accounts, or reach expired stories or viewer lists.

How to run it

  1. Paste up to five public usernames, @handles, or profile URLs into Public Instagram profiles.
  2. Optionally set Maximum current stories per profile (1-50, default 25).
  3. Click Start. Rows appear in the dataset as each account is read.

Run it from the Apify Console, on a schedule, or through the API and the Apify clients like any other actor. With empty input the actor returns exactly one clearly labeled _sample row. The sample makes no Instagram request, no proxy connection, no credential use, and no billing event.

Input

FieldPurpose
profilesUp to five public usernames, @handles, or Instagram profile URLs.
maxStoriesPerProfile1-50 current story rows for each profile; default 25.
sessionCookiesOptional. Your own Instagram account cookie. Leave it empty and the run uses the licensed data source.
proxyConfigurationEgress for requests sent to Instagram directly, i.e. the lane your own cookie uses; the default is Apify datacenter (BUYPROXIES94952). Metered groups (RESIDENTIAL, GOOGLE_SERP) are not offered and are replaced with datacenter addresses. Your own proxyUrls are used verbatim.
maxAttempts1-12 bounded account/IP attempts on that same direct lane; default 6.
requestTimeoutSeconds5-30 seconds for each Instagram request; default 15.
maxRunSeconds20-180 seconds hard run deadline; default 120.

Output

Each genuine row contains:

  • storyId, sourceUsername, sourceProfileId, and ownerUsername
  • mediaType (image, video, or carousel), imageUrl, videoUrl, and durationSeconds
  • takenAt, caption, isSponsored (the paid-partnership flag), and location when present
  • expiresAt and dimensions — populated when the run used your own cookie, null on the licensed source (see Coverage and limits)
  • A canonical storyUrl

Sample output

Two real-shaped rows — a video story and an image story tagged at a place. Values are illustrative, not from a live account, but the fields and their shapes are exactly what the actor writes.

[
{
"ok": true,
"recordType": "story",
"storyId": "3412998765432109876",
"sourceUsername": "natgeo",
"sourceProfileId": "787132",
"ownerUsername": "natgeo",
"mediaType": "video",
"takenAt": "2026-08-18T14:22:07.000Z",
"expiresAt": null,
"caption": "Sunrise over the caldera — full film out tomorrow",
"imageUrl": "https://scontent.cdninstagram.com/v/t51.36329-15/story_cover_1.jpg",
"videoUrl": "https://scontent.cdninstagram.com/o1/v/t16/story_clip_1.mp4",
"durationSeconds": 14.966,
"dimensions": null,
"isSponsored": false,
"location": null,
"storyUrl": "https://www.instagram.com/stories/natgeo/3412998765432109876/"
},
{
"ok": true,
"recordType": "story",
"storyId": "3412998765432110991",
"sourceUsername": "natgeo",
"sourceProfileId": "787132",
"ownerUsername": "natgeo",
"mediaType": "image",
"takenAt": "2026-08-18T16:05:41.000Z",
"expiresAt": null,
"caption": null,
"imageUrl": "https://scontent.cdninstagram.com/v/t51.36329-15/story_frame_2.jpg",
"videoUrl": null,
"durationSeconds": null,
"dimensions": null,
"isSponsored": true,
"location": {
"id": "213385402",
"name": "Reykjavík, Iceland",
"address": null,
"lat": 64.1466,
"lng": -21.9426
},
"storyUrl": "https://www.instagram.com/stories/natgeo/3412998765432110991/"
}
]

Rows that are not stories are labeled and never charged. An account with nothing live right now arrives like this:

{
"ok": false,
"errorCode": "NO_CURRENT_STORIES",
"error": "No current stories were returned for this public profile.",
"_notice": "This record is diagnostic only and is never charged.",
"sourceUsername": "natgeo"
}

Filter on ok === true (or recordType === "story") to keep only billable story rows.

Coverage and limits

A run is a snapshot. Stories expire about 24 hours after they are posted, so you get exactly what was live at the moment the run happened, and nothing older. There is no archive to go back to. An account with no live stories right now returns nothing, and that is a correct answer, not an error — it arrives as an uncharged NO_CURRENT_STORIES row and costs you nothing.

Two fields the licensed source does not carry. On rows served by the licensed source, expiresAt and dimensions come back null. The source publishes no expiry timestamp — and a story's 24-hour lifetime is a rule of thumb, not data, so it is left empty rather than computed and sold to you as a fact — and the payload carries no media width or height at all. Every other field on the row is populated as documented. If you need those two fields, supply your own cookie: read directly from Instagram, they are present.

Private accounts are out of reach, on every path, because nobody outside the follower list can read those stories. They are reported as an uncharged PRIVATE_OR_RESTRICTED row and no further lookup is spent on them.

Viewer lists and expired stories are not available at all. Who watched a story is visible only to the account that posted it, and a story that has already expired is gone from Instagram's public surfaces — neither is something this actor can return.

To use your own account — for the two null fields above, or to run on your own account's rate limit — put your cookie in sessionCookies:

  • In Chrome: F12 → Application → Cookies → https://www.instagram.com → copy the sessionid value. Pasting the whole cookie string works too.
  • Your cookie takes priority over everything else. The run reads Instagram directly with it, and reaches for the licensed source only for accounts that lane could not answer at all.
  • A session cookie is equivalent to an active login. Use an account you own and are authorized to automate, keep the value in Apify secrets rather than in a saved task, and revoke it through your normal account process when you are done.

Cookie values, proxy URLs, and request headers are never logged, written to the dataset, stored in this repository, or included in diagnostic rows.

Method and reliability

Without a cookie, each requested username is sent straight to the licensed source, which is addressed by username and returns the whole live tray in a single lookup. That means one lookup per account, whatever maxStoriesPerProfile is set to — the tray is not paged.

With your cookie, the username is first resolved to its numeric profile id from the public, logged-out profile endpoint, because a profile page answers a guest request and costs nothing; your cookie is spent on that step only if the guest attempt comes back gated. The tray itself is then read with the cookie, paired one-to-one with a sticky datacenter IP. A 403, 429, server error, or network failure rotates to a fresh sticky proxy session up to the configured bound. If Instagram challenges datacenter addresses, supply your own residential endpoints in proxyUrls — they are honoured exactly as given.

If that direct lane returns no stories at all, the run retries only the accounts it never got an answer for, through the licensed source. Accounts it did answer — including the ones that answered "nothing live right now" — are not looked up a second time, and no story can be billed twice: rows are deduplicated on storyId and charged once each.

Failures are named for what they are rather than flattened into one generic error: a rate limit reads as a rate limit, an upstream outage as an upstream outage, and a problem on our side is never reported as a problem on yours.

When a run stops early

A run can stop before every account is checked — the run deadline passes, or the actor reaches the per-run lookup budget set on the licensed source. That is a soft landing, never a crash: every story already collected is delivered and correct, and one uncharged PARTIAL or DEADLINE_EXCEEDED row tells you it stopped and why. Re-run to pick up the rest.

Story rows are written to the dataset before the billing event is charged. Samples, private-profile records, empty story trays, early stops, and all other diagnostics are never charged.

Pricing

$0.40 per 1,000 stories ($0.0004 each), plus $0.001 each time a run starts. Flat rate — no volume tiers, no plan gates — and you are charged only for stories actually returned.

The billed event fires once per unique story row, and only after that row has been written to the dataset. Samples, private-profile records, empty story trays, early stops, and every other diagnostic are never charged — a run that returns no stories costs only the $0.001 start fee.

FAQ

Is it legal to scrape Instagram? This actor reads only what a public Instagram account has published: it does not log in on your behalf, does not bypass a login wall, does not defeat any access control, and cannot open a private account. That said, we cannot give you legal advice and we do not promise any particular use is lawful. Stories contain personal data, so how you collect, store, and use the output is your responsibility under GDPR, CCPA, and any other law or platform terms that apply to you. If your use case is sensitive, check it with your own counsel.

Do I need an Instagram account or cookie? No. The default path uses a licensed data source and needs nothing from you. A cookie is optional and buys you two things only: the expiresAt and dimensions fields, and running on your own account's rate limit instead of the shared route.

How much does it cost, and what am I charged for? $0.40 per 1,000 stories ($0.0004 each), plus $0.001 when a run starts. You pay per unique story row actually written to the dataset. Sample rows, private-account notices, empty trays, early stops, and every other diagnostic row are free — a run that finds nothing costs only the start fee.

What happens if it returns nothing? That is usually the correct answer, not a failure. Stories expire in about 24 hours, so an account with an empty tray at the moment of the run produces an uncharged NO_CURRENT_STORIES row and no charge. If the tray was empty for a different reason, the row says which: PRIVATE_OR_RESTRICTED, NOT_FOUND, RATE_LIMITED, BLOCKED, SERVER_ERROR, or NETWORK.

Can I get stories from yesterday, or last week? No. There is no archive to read. Instagram removes a story roughly 24 hours after it is posted, and once it is gone no route reaches it. To build a history, schedule the actor and accumulate snapshots going forward.

How often should I schedule it so I do not miss stories? The story window is about 24 hours, so a run every 6-12 hours catches most trays comfortably. Runs closer together cost more only in start fees and repeat stories are deduplicated within a run, but a story posted and deleted between two runs will still be missed — nothing can recover it afterwards.

Can it scrape private accounts, or see who viewed a story? Neither. Stories from a private account are visible only to approved followers, so no path reaches them — those accounts come back as an uncharged PRIVATE_OR_RESTRICTED row. Viewer lists are visible only to the account that posted the story, and this actor never returns them.

Why are expiresAt and dimensions null? Because the licensed data source does not carry them: it publishes no expiry timestamp, and no media width or height. Rather than compute an expiry from the 24-hour rule of thumb and present a guess as data, the field is left empty. Supply your own cookie in sessionCookies and both fields are populated.

Does it download the story images and videos? No. It returns direct media URLs, not the binaries. Instagram's CDN links are time-limited, so fetch what you want to keep soon after the run rather than storing the URL and resolving it days later.

How many accounts can one run handle? Five per run, and up to 50 stories from each. The cap is deliberate: on the licensed source the tray is a single lookup per account, so five accounts is five lookups regardless of maxStoriesPerProfile. For more accounts, run the actor several times or split them across scheduled runs.

Do I need residential proxies? No. Proxy settings apply only to the direct lane your own cookie uses; the no-setup path does not go through Apify proxy at all. The default is Apify datacenter. Metered groups (RESIDENTIAL, GOOGLE_SERP) are not offered here because their per-gigabyte cost dwarfs the price of a story row, but your own proxyUrls are honoured verbatim if you want residential exits.

My run stopped before it checked every account. What happened? Either the maxRunSeconds deadline passed or the run reached the per-run lookup budget on the licensed source. Everything collected up to that point is delivered and correct, and an uncharged PARTIAL or DEADLINE_EXCEEDED row records why. Re-run to pick up the remaining accounts.

Need other Instagram data?

This actor is built for the live story tray — what a public account has on screen right now. If you need a different Instagram surface, use the dedicated actor for it:

ActorWhat it returnsPrice
Instagram Profile ScraperProfile stats and account flags, full payload$0.70 / 1,000
Instagram Profile ScraperProfile stats and account flags, lean payload$0.55 / 1,000
Instagram Mentions ScraperPosts that tagged or mentioned an account$0.44 / 1,000
Instagram Reels SearchReels found by keyword or hashtag search$0.40 / 1,000
Instagram ScraperProfiles, posts and reels from usernames or URLs — the general-purpose one$0.45 / 1,000
Instagram Hashtag ScraperPosts under any hashtag, top feed$0.40 / 1,000
Instagram Location ScraperPosts tagged at a place, ranked and recent tabs$0.40 / 1,000
Instagram Comments ScraperComments on a specific post or reel$0.40 / 1,000
Instagram Post ScraperA single post by URL, full payload$0.18 / 1,000
Instagram Post ScraperA single post by URL, lean payload$0.09 / 1,000
Instagram Reel ScraperA single reel by URL, full payload$0.35 / 1,000
Instagram Reel ScraperA single reel by URL, lean payload$0.18 / 1,000
Instagram Followers & Following ScraperFollower and following lists from a public profile$0.60 / 1,000
Instagram Transcript ScraperSpoken-word transcript of a reel or video$2.50 / 1,000
Instagram Reel UploaderPublishes a reel to your own account$0.35 per upload