Instagram Dataset Media Downloader
Pricing
Pay per usage
Instagram Dataset Media Downloader
Automatically downloads expiring media (videos and images) from Instagram Reel Scraper and Post Scraper datasets. Stores files in a named key-value store with full traceability back to the original reel or post.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Vít Tuhý
Maintained by CommunityActor stats
0
Bookmarked
15
Total users
2
Monthly active users
8 days ago
Last modified
Categories
Share
Instagram Reels and Posts Media Downloader
Downloads expiring media (videos and images) from Instagram Reel Scraper and Instagram Post Scraper datasets before the URLs expire.
Instagram CDN links are signed and short-lived. Decoding the oe expiry parameter on sampled reels gives roughly 32 hours, so run this Actor within about a day of the scrape - ideally wired to the scraper's Integrations tab so it fires automatically on every run.
The Actor grabs the media and stores it permanently in an Apify key-value store, with full traceability back to the original content.
How it works
- Reads a dataset by ID (from a Reel or Post Scraper run)
- Auto-detects whether the dataset came from the Reel Scraper or Post Scraper
- Downloads all media files (videos, images, carousel children) through Apify Proxy
- Streams each file into a key-value store
- Pushes one dataset item per file and writes a
_manifestJSON summarizing the run
Downloads run on Crawlee's FileDownload crawler, which handles concurrency autoscaling, retries with backoff, proxy session rotation, and a persistent request queue.
Source detection
| Source | Detected by | Fields downloaded |
|---|---|---|
| Instagram Reel Scraper | downloadedVideo field or productType: "clips" | videoUrl (video) |
| Instagram Post Scraper | displayUrl field present | displayUrl (image), videoUrl (video, when present), plus all childPosts media for carousel/sidecar posts |
File naming in the key-value store
Every file is keyed by the Instagram shortcode so you can trace it back to the original content:
| Source | Media | Key format | Example |
|---|---|---|---|
| Reel Scraper | Video | reel-{shortCode} | reel-CxYz123 |
| Post Scraper | Image | post-{shortCode}-image | post-AbCd456-image |
| Post Scraper | Video | post-{shortCode}-video | post-AbCd456-video |
| Post Scraper | Carousel child image | post-{childShortCode}-image | post-EfGh789-image |
| Post Scraper | Carousel child video | post-{childShortCode}-video | post-EfGh789-video |
Proxy
Instagram fingerprints datacenter IP ranges. Running unproxied sends requests straight from Apify platform IPs, which Meta can rate-limit and attribute back to Apify infrastructure. The Actor therefore defaults to Apify Proxy with the RESIDENTIAL group.
If your account has no residential access, the Actor falls back to automatic proxy group selection, and then to no proxy at all - each step logs a warning rather than failing the run. Check the log if downloads start failing.
Migration safety
Apify can move a running Actor between servers at any time. This Actor is built to survive that:
- Downloads live in a persistent request queue, so files already downloaded are not fetched again after a restart
- Requests are deduplicated on the media key, not the URL, because Instagram CDN URLs carry expiring signed query parameters that change between reads
- Run counters use Crawlee's
useState, which is persisted to the key-value store on themigratingevent - The
_manifestis rebuilt from the Actor's own dataset at the end of the run, not from an in-memory array, so it stays complete across a migration
Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
datasetId | String | Yes | - | ID of the dataset to read media URLs from |
proxyConfiguration | Object | No | Apify Proxy, RESIDENTIAL | Proxy used for downloads |
keyValueStoreName | String | No | run's default KVS | Name of the key-value store to save media into |
maxConcurrency | Integer | No | 5 | Upper bound on parallel downloads (1-50) |
maxRequestRetries | Integer | No | 3 | Retries for non-proxy failures such as timeouts (0-10) |
maxSessionRotations | Integer | No | 5 | Retries with a fresh proxy IP after a proxy error or block (0-20) |
Crawlee counts these two budgets separately - a proxy error rotates the session and does not consume a request retry. Rotations happen with no backoff, so a URL whose CDN edge rejects your proxy type will exhaust the rotation budget within a couple of seconds and then be recorded as failed.
concurrency is still accepted as an alias for maxConcurrency so integrations built against v1.x keep working.
Example input
{"datasetId": "{{resource.defaultDatasetId}}","proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]},"maxConcurrency": 10}
Output
The Actor pushes one dataset item per media file:
{"shortCode": "CxYz123","originalId": "3210987654321","ownerUsername": "natgeo","caption": "Amazing wildlife footage...","timestamp": "2026-06-20T12:00:00.000Z","postType": "Video","kvsKey": "reel-CxYz123","mediaType": "video","sourceUrl": "https://...","contentType": "video/mp4","sizeBytes": 5242880,"status": "ok"}
Files that fail every retry are pushed with "status": "failed" and an error field instead of contentType/sizeBytes. For carousel children, items also include parentShortCode and childIndex.
The same array is written to the _manifest key in the media key-value store.
Downloaded files can be accessed via:
- Apify Console: Storage > Key-value stores
- API:
https://api.apify.com/v2/key-value-stores/{storeId}/records/{key}
Integration setup
The primary use case is running this Actor automatically after a Reel or Post Scraper run finishes.
- Open your Instagram Reel Scraper or Instagram Post Scraper Actor (or Task) in Apify Console
- Go to the Integrations tab
- Click Add integration > Run Actor
- Configure:
- When: Run succeeded
- Actor: Select
instagram-media-downloader(this Actor) - Input:
{"datasetId": "{{resource.defaultDatasetId}}"}
- Click Save
{{resource.defaultDatasetId}} is replaced with the dataset ID from the scraper run that triggered the integration. It is only substituted by the integration system - if you run this Actor standalone and leave the template in the field, the run fails immediately with an explanatory message.
Setting up on a Task (recommended)
If you run the scraper via a saved Task (e.g. "Scrape NatGeo reels daily"), open the Task, go to its Integrations tab, and follow the same steps. Every scheduled or manual run of that Task will then trigger the media download.
Custom key-value store name
{"datasetId": "{{resource.defaultDatasetId}}","keyValueStoreName": "natgeo-reels-{{resource.id}}"}
{{resource.id}} is the run ID of the source scraper. Named stores are retained indefinitely; the default store follows the run's retention period.
Data retention
Storage counts toward your Apify plan limits. To manage it:
- Delete old key-value stores via Console or API when you no longer need the media
- Use the
_manifestkey to audit what is stored before cleanup
Limitations
- URLs that expired before the run will fail after all retries; the Actor logs a warning and continues
- The Actor does not re-upload to external storage (S3, GCS, etc.) - files stay in Apify KVS
- Very large datasets need proportionally more run time; memory stays flat because downloads are streamed