Instagram Story Details Scraper With Media Details
Pricing
from $2.99 / 1,000 results
Instagram Story Details Scraper With Media Details
📸 Instagram Story Details Scraper extracts public Story metadata—timestamps, media URLs, captions, hashtags, mentions, locations & link stickers—for fast analysis and archiving. 🔍 SEO-ready insights for social listening, brand monitoring & reporting. ⚡ Compliant, reliable.
Pricing
from $2.99 / 1,000 results
Rating
0.0
(0)
Developer
SimpleAPI
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
0
Monthly active users
a day ago
Last modified
Categories
Share
Instagram Story Scraper — Media Assets, Bitrate & CDN Expiry
Instagram Story Scraper collects every active story a profile is currently running and returns one JSON row per story — the full raw Instagram story payload plus a normalized mediaAssets ladder: quality, resolution, bitrate, codec, and the exact CDN expiry timestamp for every video and photo rendition Instagram generated. Social media managers, brand-safety and archival teams, and automated monitoring pipelines use it to capture story creative — and know exactly when the media URL dies — before Instagram removes it. Point it at a list of profiles and watch rows land in the dataset as they're collected.
📇 What is Instagram Story Scraper?
Instagram Story Scraper is an Apify Actor that logs in with your own Instagram session cookie and pulls the current story tray for a list of profiles, returning each story as a structured JSON record. It requires a valid, logged-in sessionid cookie from instagram.com — the actor acts as your account, so it only sees stories your account could already see. Results are written to an Apify dataset that exports to JSON, CSV, Excel, HTML table, or XML.
- 📇 Batch discovery — list any number of full profile URLs or plain usernames in one run
- 🎞️ Story type filtering — keep photo-only, video-only, or both with
mediaType - 📶 Full rendition ladder —
mediaAssets[]normalizes every video and image quality Instagram generated for the story, sorted highest-resolution first - 📊 DASH manifest parsing — real
bitrate/codecvalues per rendition instead of nulls, plus adashManifestInfoquality-count and max-bitrate summary - ⏱️ CDN expiry decode — every media asset carries its own
expiresAt(UTC ISO-8601) andexpiresAtTimestamp, decoded from that asset's own CDN URL - 🌍 Apify Proxy support — proxy input is honored on every request, with automatic escalation if a request gets blocked
🎬 What data can I extract with Instagram Story Scraper?
Each story row carries the full Instagram story payload — identity, content, engagement flags, and this Actor's own media-quality enrichment — as one flat JSON object.
| Field | Example Value | Use Case |
|---|---|---|
pk | "3439821097654321098" | Unique story identifier for dedup across runs |
id | "3439821097654321098_25025320" | Combined media + owner ID |
code | "Cx9k2LhOZ1A" | Shortcode used in the story's share link |
taken_at | 1753861200 | Unix timestamp the story was posted |
expiring_at | 1753947600 | Unix timestamp Instagram will expire the story |
media_type | 2 | 1 = photo, 2 = video (raw Instagram value) |
product_type | "story" | Instagram's own content-type label |
user | {"pk": "25025320", "username": "kyliejenner", ...} | Owning profile, for grouping multi-profile runs |
caption | null | Story caption text, when present |
video_versions | [{"width":1080,"height":1920,"url":"..."}] | Raw Instagram rendition array (unmodified, base fields kept) |
image_versions2 | {"candidates":[{"width":1080,...}]} | Raw Instagram photo rendition array |
video_duration | 14.5 | Story length in seconds, raw field |
has_audio | true | Whether the video story has an audio track |
mediaAssets | see below | This Actor's normalized quality/bitrate/expiry ladder |
mediaAssetCount | 3 | Row count of mediaAssets for this story |
dashManifestInfo | {"qualityCount":4,"maxBitrate":2934821} | Parsed DASH manifest summary |
dashQualityCount | 4 | Number of video qualities in the DASH manifest |
dashMaxBitrateBps | 2934821 | Highest bitrate (bits/sec) across DASH renditions |
videoDurationSeconds | 14.5 | Mirrors video_duration as a clean top-level column |
hasAudio | true | Mirrors has_audio as a clean top-level column |
originalWidth / originalHeight | 1080 / 1920 | Mirrors original_width/original_height |
music_metadata | null | Attached music track info, when present |
can_reply / can_reshare | true / true | Viewer permission flags Instagram returns for the story |
is_paid_partnership | false | Branded-content flag |
📶 Media rendition and delivery specs
mediaAssets[] is this Actor's own feature: every video_versions and image_versions2 rendition is flattened into one list of {type, quality, width, height, bitrate, codec, url, expiresAt, expiresAtTimestamp} rows, sorted highest-resolution first, video group then image group. bitrate/codec come from matching the rendition's width/height against the parsed video_dash_manifest (dashManifestInfo) where a match exists — never fabricated. A buyer who needs "the best copy of this story, and exactly when the link dies" reads mediaAssets[0].url and mediaAssets[0].expiresAt directly, without touching the raw DASH XML.
🎯 Story identity and targeting fields
pk, id, code, taken_at, expiring_at, media_type, and product_type identify and de-duplicate story rows across repeated runs against the same profiles. user groups rows back to the profile that posted them in multi-profile batches. media_type (raw 1/2) and the mediaType input filter together let you segment photo stories from video stories without touching mediaAssets.
🧩 Why not build this yourself?
Instagram does not publish a public API for pulling another account's current stories with full media renditions — the Graph API only exposes limited insights for Business/Creator accounts you manage yourself, not arbitrary profiles' story media. Building this yourself means maintaining a logged-in session cookie, handling Instagram's block responses, and parsing a DASH/MPD XML manifest just to get a real bitrate instead of a null.
This Actor already does that work: sessionId-based login (see build_session), a three-tier proxy escalation on blocked responses (your configured proxy, then an Apify SHADER pool, then RESIDENTIAL proxies) implemented in fetch_with_fallback, and a regex-based DASH manifest parser (parse_dash_manifest) that reads <Representation> attributes without needing a namespace-aware XML library. No media file is ever downloaded — only the JSON/XML metadata the story-tray response already contains is parsed.
If you only need insights for stories on your own connected Business/Creator account, Meta's own Creator Studio or Graph API insights cover that without any scraping. For bulk coverage across other profiles, or for the rendition/bitrate/expiry detail those tools don't expose, this Actor is the practical path.
👥 How to use data extracted from Instagram stories?
📊 Social and influencer marketing teams
Point the actor at a list of brand, competitor, or creator profiles with mediaType: "video" and videoQuality: "highest-only" to pull the single best-quality rendition of every active video story. Read mediaAssets[0].url and expiresAt to grab the creative before Instagram removes it, and dashMaxBitrateBps to confirm you captured the top-quality copy rather than a compressed fallback.
🏢 Agencies and service providers
Agencies running the same profile list on a recurring Apify schedule get a fresh row per story per run, keyed on pk/code, so repeated collections for multiple clients de-duplicate cleanly against prior runs without extra bookkeeping.
🔬 Market research and intelligence
Researchers studying story-format adoption use media_type, dashQualityCount, and videoDurationSeconds across a batch of profiles to compare how many qualities each account's stories are encoded in and how photo/video mix differs across accounts, without opening a single story manually.
🤖 AI agents and automated pipelines
Because each story is pushed to the dataset the moment it's collected, an agent or webhook-driven pipeline can poll the dataset mid-run and react to new mediaAssets entries as they land — useful for near-real-time brand-monitoring bots built on the Apify API rather than the Instagram app itself.
🔼 Input sample
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
urls | Yes | array | One list, many profiles — paste full profile links or plain usernames (with or without @). Add more than one to batch a run. | ["https://www.instagram.com/kyliejenner/", "natgeo"] |
sessionId | Yes | string | Your logged-in sessionid cookie from instagram.com. Default "". | "58123456789%3Aabc...%3A12%3AAYc..." |
sessionid | No | string | Legacy alias for sessionId, for backward compatibility with older input JSON. Default "". | "" |
mediaType | No | string | Keep only photo stories, only video stories, or all of them. Enum: all (All story types), photo (Photo stories only), video (Video stories only). Default "all". | "video" |
videoQuality | No | string | all keeps every video rendition; highest-only keeps just the single highest-resolution rendition per video story. Enum: all, highest-only. Default "all". | "highest-only" |
includeDashSpecs | No | boolean | Parses video_dash_manifest into dashManifestInfo and fills real bitrate/codec on matching mediaAssets rows. Default true. | true |
proxyConfiguration | No | object | Apify Proxy configuration, honored by every request the run makes. | {"useApifyProxy": true} |
{"urls": ["https://www.instagram.com/kyliejenner/", "natgeo"],"sessionId": "58123456789%3Aabcdefghij1234567890%3A12%3AAYc...","mediaType": "video","videoQuality": "highest-only","includeDashSpecs": true,"proxyConfiguration": { "useApifyProxy": true }}
Common pitfall: mediaType filters which already-fetched stories are pushed to the dataset — it does not reduce the number of requests the actor makes per profile, so setting it to "video" will not speed up a run against a photo-heavy account, only shrink the final row count. Also leave sessionid (lowercase) blank once you've filled sessionId — the actor reads sessionId first and only falls back to the legacy alias if it's empty.
🔽 Output sample
Every story is a typed, normalized JSON object with a consistent key set across runs. Export straight from the Apify dataset as JSON, CSV, Excel, HTML table, or XML — no separate conversion step.
{"pk": "3439821097654321098","id": "3439821097654321098_25025320","is_visual_reply_commenter_notice_enabled": false,"like_and_view_counts_disabled": false,"is_post_live_clips_media": false,"is_reshare_of_text_post_app_media_in_ig": false,"is_reel_media": false,"fbid": null,"device_timestamp": 1753861200123456,"caption_is_edited": false,"strong_id__": "18089123456789012","is_quicksnap_recap": false,"deleted_reason": 0,"expiring_at": 1753947600,"mezql_token": null,"should_request_ads": false,"is_terminal_video_segment": false,"integrity_review_decision": "pending","client_cache_key": "MzQzOTgyMTA5NzY1NDMyMTA5OA==","has_privately_liked": false,"filter_type": 0,"taken_at": 1753861200,"commenting_disabled_for_viewer": false,"hide_view_all_comment_entrypoint": false,"is_comments_gif_composer_enabled": false,"has_liked": false,"video_sticker_locales": [],"is_dash_eligible": 1,"video_dash_manifest": "<?xml version=\"1.0\"?><MPD>...</MPD>","video_codec": "h264","number_of_qualities": 4,"video_versions": [{ "width": 1080, "height": 1920, "url": "https://scontent.cdninstagram.com/o1/video1080.mp4?oe=66B1F2A3" },{ "width": 480, "height": 852, "url": "https://scontent.cdninstagram.com/o1/video480.mp4?oe=66B1F2A3" }],"video_duration": 14.5,"has_audio": true,"can_viewer_save": true,"shop_routing_user_id": null,"is_organic_product_tagging_eligible": false,"product_suggestions": [],"can_see_insights_as_brand": false,"media_type": 2,"code": "Cx9k2LhOZ1A","caption": null,"fundraiser_tag": null,"sharing_friction_info": { "should_have_sharing_friction": false, "bloks_app_url": null },"timeline_pinned_user_ids": [],"has_translation": false,"original_media_has_visual_reply_media": false,"coauthor_producers": [],"coauthor_producer_can_see_organic_insights": null,"invited_coauthor_producers": [],"is_in_profile_grid": false,"profile_grid_control_enabled": false,"user": { "pk": "25025320", "username": "kyliejenner", "full_name": "Kylie Jenner", "profile_pic_url": "https://scontent.cdninstagram.com/o1/profile.jpg" },"image_versions2": { "candidates": [{ "width": 1080, "height": 1920, "url": "https://scontent.cdninstagram.com/o1/photo1080.jpg?oe=66B1F2A3" }] },"original_width": 1080,"original_height": 1920,"product_type": "story","is_paid_partnership": false,"music_metadata": null,"organic_tracking_token": "eyJ2ZXJzaW9uIjo1fQ==","ig_media_sharing_disabled": false,"crosspost": null,"crosspost_metadata": null,"boost_unavailable_identifier": null,"boost_unavailable_reason": null,"boost_unavailable_reason_v2": null,"subscribe_cta_visible": false,"is_cutout_sticker_allowed": true,"cutout_sticker_info": null,"can_hype": false,"gen_ai_detection_method": null,"fb_aggregated_like_count": 0,"fb_aggregated_comment_count": 0,"has_high_risk_gen_ai_inform_treatment": false,"open_carousel_show_follow_button": false,"is_tagged_media_shared_to_viewer_profile_grid": false,"should_show_author_pog_for_tagged_media_shared_to_profile_grid": false,"is_open_to_public_submission": false,"media_attributions_data": null,"archive_story_deletion_ts": null,"can_send_prompt": false,"is_first_take": false,"is_from_discovery_surface": false,"is_rollcall_v2": false,"supports_reel_reactions": true,"is_photo_mash_story": false,"can_play_spotify_audio": false,"is_superlative": false,"show_one_tap_fb_share_tooltip": false,"story_feed_media": [],"story_music_stickers": [],"can_reply": true,"is_viewer_mentioned": false,"can_reshare": true,"mediaAssets": [{"type": "video","quality": "1920p","width": 1080,"height": 1920,"bitrate": 2934821,"codec": "avc1.640020","url": "https://scontent.cdninstagram.com/o1/video1080.mp4?oe=66B1F2A3","expiresAt": "2026-07-31T02:00:00Z","expiresAtTimestamp": 1785556800},{"type": "video","quality": "852p","width": 480,"height": 852,"bitrate": 891234,"codec": "avc1.42001f","url": "https://scontent.cdninstagram.com/o1/video480.mp4?oe=66B1F2A3","expiresAt": "2026-07-31T02:00:00Z","expiresAtTimestamp": 1785556800},{"type": "image","quality": "1920p","width": 1080,"height": 1920,"bitrate": null,"codec": null,"url": "https://scontent.cdninstagram.com/o1/photo1080.jpg?oe=66B1F2A3","expiresAt": "2026-07-31T02:00:00Z","expiresAtTimestamp": 1785556800}],"mediaAssetCount": 3,"dashManifestInfo": { "qualityCount": 4, "maxBitrate": 2934821 },"dashQualityCount": 4,"dashMaxBitrateBps": 2934821,"videoDurationSeconds": 14.5,"hasAudio": true,"originalWidth": 1080,"originalHeight": 1920}
The default Output tab view surfaces 11 of these columns (pk, id, code, media_type, mediaAssetCount, dashQualityCount, dashMaxBitrateBps, videoDurationSeconds, hasAudio, taken_at, user) for a quick scan — every field above is present in the full row and in JSON/CSV/Excel exports.
🎯 How do you filter and target specific Instagram stories?
There's no category or free-text search here — targeting starts with the urls list itself: batch as many profile URLs or usernames as you need in one run rather than running one actor call per profile. From there, three real filters narrow what lands in the dataset. mediaType (all / photo / video) drops rows of the type you don't want after they're fetched. videoQuality (all / highest-only) controls how many rows each video story contributes to mediaAssets — highest-only keeps just the top rendition, useful when you only need one usable copy per story. includeDashSpecs trades a faster run (no manifest parsing) for real bitrate/codec values instead of nulls on video renditions. There is no limit/maxItems input — every currently active story item Instagram's API returns for each profile you list gets processed; the real ceiling on volume is how many profiles you list and how many active stories those profiles currently have, not a setting in this actor.
{ "urls": ["kyliejenner", "natgeo", "nike", "nasa", "hm"], "sessionId": "<your_sessionid>", "mediaType": "all", "videoQuality": "all", "includeDashSpecs": true }
{ "urls": ["kyliejenner"], "sessionId": "<your_sessionid>", "mediaType": "video", "videoQuality": "highest-only", "includeDashSpecs": false }
{ "urls": ["nike"], "sessionId": "<your_sessionid>", "mediaType": "video", "videoQuality": "all", "includeDashSpecs": true }
▶️ Want to try other Instagram scrapers?
| Scraper Name | What it extracts |
|---|---|
| Instagram Posts Scraper With Tagged Places & Coordinates | Instagram posts with tagged-place names and coordinates |
| Instagram Profile Post Scraper With Engagement Analytics | Profile posts with engagement analytics |
| Snapchat User Stories Scraper With Creator Profile Insights | Snapchat user stories and creator profile insights |
| TikTok User Profile Scraper: Business Leads | TikTok profile data for lead generation |
🐍 How to extract Instagram story data programmatically
This Actor runs as a standard Apify Actor — one API call starts the run, and structured JSON comes back from the dataset. No Instagram developer account or API key is involved.
Python example
from apify_client import ApifyClientclient = ApifyClient("<APIFY_API_TOKEN>")run = client.actor("<your-username>/instagram-story-details-scraper-with-media-details").call(run_input={"urls": ["https://www.instagram.com/kyliejenner/"],"sessionId": "<your_instagram_sessionid_cookie>","mediaType": "video","videoQuality": "highest-only",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["code"], item["mediaAssetCount"], item["mediaAssets"][0]["expiresAt"])
Export to spreadsheets or CRM
Export the dataset directly to CSV or Excel from the Apify Console or API and map columns straight from the JSON keys — code and pk as record IDs, mediaAssetCount and dashMaxBitrateBps as quality columns, user.username for the owning profile — into a spreadsheet or CRM import without any reshaping.
⚖️ Is it legal to scrape Instagram stories?
Scraping publicly viewable Instagram stories is generally legal — the data is already displayed to any account permitted to view it. This Actor changes the access model, not the legality question: it logs in as your own Instagram account via sessionId, so it only ever retrieves stories your account could already see when browsing normally. Story rows contain personal data (usernames, profile info, captions), so GDPR and CCPA govern how you store and use it once collected — you need a lawful basis for any bulk storage of that personal data. Running automated access under your own account also carries Instagram's own Terms of Service risk to that account, separate from the data-protection question. Consult legal counsel for commercial applications involving bulk storage of personal data.
❓ FAQ
Do stories disappear before I can save them?
Yes — this actor has no history or archive lookup. It returns only what Instagram's reels_media endpoint currently serves for each profile at request time; once Instagram removes a story from that profile, this actor can no longer retrieve it. Run it on a schedule against the profiles you're monitoring if you need to catch stories before they're gone.
Can I get the video quality ladder and bitrate along with each story?
Yes. Every story row includes mediaAssets[] — one entry per rendition with quality, width, height, bitrate, codec, url, and expiresAt/expiresAtTimestamp — plus a dashManifestInfo summary (qualityCount, maxBitrate). Turn off includeDashSpecs if you don't need real bitrate/codec values and want a faster run.
How accurate is the CDN expiry (expiresAt) data?
It's exact, not estimated. expiresAt and expiresAtTimestamp are decoded directly from the oe= hex expiry parameter Instagram itself embeds in each asset's CDN URL, so they reflect exactly when that specific URL will stop resolving.
How many story records can I get per run?
There is no limit/maxItems input on this actor. It pushes every currently active story item Instagram returns for each profile listed in urls, filtered only by mediaType. Volume scales with how many profiles you list and how many active stories those profiles currently have.
What do mediaType and videoQuality actually control?
mediaType drops story rows of the type you don't want (photo or video) after they're fetched. videoQuality controls how many renditions each video story contributes to mediaAssets — all keeps every rendition Instagram generated, highest-only keeps just the top one.
Why does the output have so many fields I don't recognize?
Most of them are Instagram's own internal bookkeeping flags, returned verbatim from the private mobile API the actor calls (things like is_rollcall_v2 or is_photo_mash_story), and kept for completeness rather than guessed at. In practice, most runs only need the identity, content, and mediaAssets/dashManifestInfo fields documented above.
Does Instagram Story Scraper work with Claude, ChatGPT, and AI agent frameworks?
It has no dedicated MCP server. It's callable as a standard Apify Actor over the Apify API from any agent framework that can make an HTTP call or use the apify_client SDK, then read the JSON rows back from the dataset.
Can I use Instagram Story Scraper without an Instagram API key or developer account?
Yes. You need a valid, currently logged-in sessionid cookie from a normal instagram.com browser session — no Meta developer app, no API key, no OAuth flow.
Conclusion
Instagram Story Scraper turns Instagram's raw, unstructured story payload into one consistent JSON row per story — identity fields, engagement flags, and a normalized mediaAssets ladder with real bitrate, codec, and CDN expiry per rendition. It's built for teams who need story creative and its technical delivery specs before Instagram removes it, not just a screenshot of what was there. Add your profile list and sessionId in the Apify Console, hit Start, and watch rows arrive in the Output tab as they're collected.