# Instagram Downloader API - Reels, Video & Photo (`dataspryva/instagram-downloader-api-reels-video-and-photo`) Actor

Download Instagram photos, videos, Reels and carousel posts from post links or usernames, up to 100 of each per run. Public posts and profiles need no login; add your own cookies for age-restricted posts and private accounts you follow. Each file gets a download link, size, resolution and length.

- **URL**: https://apify.com/dataspryva/instagram-downloader-api-reels-video-and-photo.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** Social media, Videos, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Instagram Downloader API - Reels, Video & Photo

Save the actual image and video files behind Instagram posts, Reels, IGTV videos and carousels. Paste post links, list a few usernames, or do both, and each file is stored in your run's key-value store with a ready-to-use download link. The dataset adds a record for every file with its source post, owner, file type, dimensions, size and, for videos, length, frame rate and bitrate. It is built for people who need a clean local copy of public Instagram media together with the details needed to catalogue it.

### What this actor does

- **Downloads from post links:** accepts `/p/`, `/reel/`, `/reels/` and `/tv/` URLs, with or without the username in the path
- **Downloads from usernames:** finds the most recent posts and Reels on each profile (up to 100 per profile) and downloads them
- **Splits carousels:** every photo or clip in a multi-item post becomes its own file and its own record
- **Describes every file:** width, height, aspect ratio, extension and size, plus duration, frame rate, codec and bitrate for videos
- **Gives you a download link:** each file is saved under a stable key with a public URL you can open or fetch from your own tools

### Authentication

Public posts are downloaded without signing in, and so are the posts of any public profile, however many you ask for: they are listed the way Instagram lists them for a visitor who is not signed in. A profile is always read that way first, even when you give cookies. Only a signed-in view can open posts behind an age check and private accounts your account follows; for those, paste your own Instagram cookies into the optional `cookies` field (see Using your own cookies below). Without cookies those cases are reported with their status. The actor does not sign in with any account of its own.

### Output per media file

One dataset record is created per downloaded file. A carousel with 5 items produces 5 records. A post that could not be downloaded produces one short record with only its `post_url` and a `status` that names the cause. The explanation, which post it was and what you can do about it, is in the run log, one line per post, followed by a closing `Done:` summary. While the run is going, the status message of the run shows a live count such as `Downloaded 12/20 posts - 2 need a sign-in`.

- `status` – `success` for a downloaded file, otherwise the reason it failed (see the status table below)
- `filename` – saved file name including extension, named after the media ID (e.g. `3918833148741120108.jpg`)
- `post_url` – the Instagram post the file came from
- `username` – Instagram handle of the post owner
- `type` – `image` or `video` (`unknown` if the file format is not recognised)
- `download_status` – `finished` once the file has been saved
- `downloaded_at` – timestamp of the download
- `storage_key` – key of the file in the key-value store
- `download_url` – public link to the stored file
- `media_meta_data` – technical details of the file:
  - `width`, `height` – size in pixels
  - `aspect_ratio` – width divided by height, rounded to 2 decimals
  - `ext` – file extension (`jpg`, `mp4`, ...)
  - `filesize_bytes` – size on disk in bytes
  - `fps` – frames per second (videos only)
  - `duration` – length in seconds (videos only)
  - `video_codec` – codec identifier such as `avc1` (videos only)
  - `total_bitrate_kbps` – overall bitrate in kilobits per second (videos only)

Any technical detail that cannot be determined for a file is returned as `null`.

**Status of a post or profile that gave no files.** The record holds the `post_url` of the post (or, for a username, `authorMeta` with the `username` and `profile_url`) and one of these `status` values:

| `status` | Meaning |
|---|---|
| `post_age_restricted` | The post sits behind an age check; it opens only with `cookies` of an account that may view it |
| `post_is_private` | The post is in a private account; it opens only with `cookies` of an account that follows it |
| `post_not_found` | The post was deleted, never existed, or is visible only to signed-in users |
| `profile_not_found` | The profile does not exist, or was deleted or suspended |
| `profile_is_private` | The profile is private; it opens only with `cookies` of an account that follows it |
| `profile_age_restricted` | The profile sits behind an age check ("You must be 18 years old or over"); it opens only with `cookies` of an account that is old enough |
| `profile_restricted` | Instagram hides the profile from visitors who are not signed in ("unavailable for certain audiences"); it may open with `cookies` |
| `blocked` | Instagram kept refusing the request after several tries; run it again later |
| `error` | Something unexpected went wrong; the log has the details |
| `invalid_url` | The entry is not an Instagram post link |
| `no_media` | The post has no photo or video link |
| `download_failed` | The post was read but none of its files could be downloaded |
| `no_posts` | A username you listed has no posts |
| `cookies_unusable` | A private username needed your `cookies` and Instagram did not accept them or could not be reached to check them |

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `postUrls` | array | – | Instagram post, Reel or IGTV links to download, up to 10,000. Accepts `/p/`, `/reel/`, `/reels/`, `/tv/` and `username/p/shortcode` formats. Duplicate links are merged into one record. |
| `usernames` | array | – | Instagram usernames whose recent posts should be downloaded (up to 100). Each entry can be a username, `@username` or a profile link such as `https://www.instagram.com/natgeo/` |
| `maxPosts_per_username` | integer | `10` | How many recent posts to download for each username. Range 0–100; `0` downloads nothing from usernames. Posts left out by the two filters below do not count. Public profiles need no sign-in; a private profile needs your `cookies` |
| `newerThan` | string | – | Optional date (`YYYY-MM-DD`). Posts of a username published before it are skipped. Empty means no date filter. It must be from 2000-01-01 up to the day before the run, otherwise it is ignored with a warning in the log. Does not apply to links you give. The date is read from the post's id, so it can be off by up to a day |
| `skipPinnedPosts` | boolean | `false` | Leave out the posts a profile has pinned to the top of its grid. Does not apply to links you give. Instagram does not mark pinned posts for a visitor who is not signed in, so they are recognised by their place and date; a pinned post that is also the profile's newest post cannot be told apart and stays in |
| `cookies` | string | – | Optional. Your own Instagram cookies as JSON, used for age-restricted posts and private accounts your account follows. It is a secret input, so the Apify Console hides and encrypts it |

Provide at least one of `postUrls` or `usernames`. Posts listed in `postUrls` are always downloaded, regardless of `maxPosts_per_username`.

#### Example: back up one Reel and one photo post

```json
{
  "postUrls": [
    "https://www.instagram.com/natgeo/reel/DYhkH24lf3j/",
    "https://www.instagram.com/leomessi/p/DZifppZj_cB/"
  ]
}
```

#### Example: latest posts from a few official accounts

```json
{
  "usernames": ["natgeo", "nasa", "espnfc"],
  "maxPosts_per_username": 5
}
```

#### Example: deep archive of one brand account

```json
{
  "usernames": ["nike"],
  "maxPosts_per_username": 100
}
```

#### Example: fixed links plus a fresh batch from one profile

```json
{
  "postUrls": ["https://www.instagram.com/leomessi/p/DZifppZj_cB/"],
  "usernames": ["natgeo"],
  "maxPosts_per_username": 3
}
```

### Using your own cookies

Cookies are optional and only used for what a signed-out visitor cannot see. To add them, export your instagram.com cookies as JSON from a browser where you are signed in, using any cookie-export browser extension, and paste the JSON into the `cookies` field. The field is a secret input, so the Apify Console hides and encrypts it, and the cookies are used only during your run. Before anything is downloaded with them, the actor asks Instagram whether it recognises the cookies; if they are invalid, expired or signed out, the run log says so and the affected posts are reported without a download. Only paste cookies from an account that belongs to you, and only collect what you are allowed to access. You are responsible for complying with Instagram's terms and applicable law, and a dedicated account is the safer choice.

#### Cookie format

The `cookies` field takes a **JSON array** of cookie objects, the format most cookie-export browser extensions call "JSON". Other export formats are not supported: a Netscape `cookies.txt` file or a raw `Cookie:` header string will be rejected. Each cookie needs at least `name`, `value` and `domain`, and the export must include the `sessionid` and `ds_user_id` cookies of the signed-in account. A cookie that has an expiry date (`expirationDate`, in seconds since 1970) must not be expired.

One cookie looks like this (the value shown is a placeholder, paste your own export unchanged):

```json
[
  {
    "name": "sessionid",
    "value": "<your sessionid value>",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": null,
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  }
]
```

<details>
<summary>Show a complete example export (placeholder values)</summary>

```json
[
  {
    "name": "ps_n",
    "value": "1",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "datr",
    "value": "EXAMPLE-datr-value",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "ig_nrcb",
    "value": "1",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": false,
    "sameSite": null,
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "ds_user_id",
    "value": "1234567890",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": false,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "csrftoken",
    "value": "EXAMPLE-csrftoken-value",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": false,
    "sameSite": null,
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "ig_did",
    "value": "00000000-0000-0000-0000-000000000000",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "ps_l",
    "value": "1",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "lax",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "wd",
    "value": "1280x720",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": false,
    "sameSite": "lax",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "mid",
    "value": "EXAMPLE-mid-value",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "sessionid",
    "value": "<your sessionid value>",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": null,
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "dpr",
    "value": "1",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": false,
    "sameSite": "no_restriction",
    "session": false,
    "firstPartyDomain": "",
    "partitionKey": null,
    "expirationDate": 1893456000,
    "storeId": null
  },
  {
    "name": "rur",
    "value": "EXAMPLE-rur-value",
    "domain": ".instagram.com",
    "hostOnly": false,
    "path": "/",
    "secure": true,
    "httpOnly": true,
    "sameSite": "lax",
    "session": true,
    "firstPartyDomain": "",
    "partitionKey": null,
    "storeId": null
  }
]
```

</details>

Treat the cookies like a password: they give access to your Instagram account, so never share them or post them publicly.

### Example output

```json
{
  "status": "success",
  "filename": "3918833148741120108.jpg",
  "post_url": "https://www.instagram.com/leomessi/p/DZifppZj_cB/",
  "username": "leomessi",
  "type": "image",
  "download_status": "finished",
  "downloaded_at": "2026-07-02T18:46:36.768000",
  "storage_key": "3918833148741120108.jpg",
  "download_url": "https://api.apify.com/v2/key-value-stores/abc123/records/3918833148741120108.jpg",
  "media_meta_data": {
    "width": 1080,
    "height": 1350,
    "ext": "jpg",
    "filesize_bytes": 284672,
    "aspect_ratio": 0.8
  }
}
```

### Known limitations

- **Private accounts need cookies:** a private username or a post from a private account opens only with the cookies of an account that follows it. Without cookies, or with cookies of an account that does not follow it, you receive a record with the status `post_is_private` (a post) or `profile_is_private` (a username) instead.
- **No Stories:** only permanent posts, Reels and IGTV videos are downloaded.
- **Stored files expire:** files live in the run's key-value store and follow your Apify plan's data retention. Export anything you need to keep for the long term.
- **Profile depth:** a profile yields at most 100 posts per run, and the number actually available depends on how many posts Instagram shows for that profile.

### Use cases

- **Social media managers:** keep an offline backup of your brand's own posts and Reels before editing or deleting them
- **Content and creative teams:** collect user-generated photos and clips from a campaign for review and rights clearance
- **Digital archivists and librarians:** preserve public cultural, news or institutional accounts together with file-level details
- **Academics and journalists:** build a media sample from named public accounts for research or analysis
- **Video editors and agencies:** pull reference footage and measure resolution, duration and bitrate before planning edits
- **Competitive analysts:** review the visual style and format mix (photo, carousel, Reel) of rival accounts

### Responsible use

Instagram posts that anyone can see can still show or identify real people, and the files this actor saves (faces, voices, locations, captions burned into images) are personal data in many cases. Before downloading, make sure you have a lawful basis (for example under the GDPR) for keeping and processing that media, limit what you keep to what you need, and honour requests from the people shown. Do not use downloaded media to profile, track or target minors, or to single out people by special categories of personal data such as health, religion, political opinion or sexual orientation.

Photos and videos downloaded by this actor belong to their creators. Make sure you have the rights or permission for any reuse.

### FAQ

#### Do I need an Instagram account or cookies to use this actor?

No. Public posts need no sign-in, and neither do the posts of a public username. Only age-restricted posts and private accounts need cookies, and you can paste your own in the optional `cookies` field.

#### Do I need to set up a proxy?

No. The actor chooses a working connection to Instagram automatically. Just fill in the input and run it.

#### Can I download from private accounts?

Yes, with your own cookies from an account that follows the private account. This works for a username (its posts are listed in a signed-in browser) and for a post link. Without cookies, or with cookies of an account that does not follow it, the dataset gets a record with the status `post_is_private` or `profile_is_private` and no media.

#### Are carousel posts supported?

Yes. Each photo or video in a carousel is downloaded separately and gets its own dataset record and its own key-value store entry, so a 5-item carousel gives you 5 files.

#### What happens if I set `maxPosts_per_username` to 0?

No posts are collected from the usernames. Links in `postUrls` are still downloaded. Use this when you only want to download specific posts.

#### Where do I find the downloaded files?

Open the run's key-value store: every file is saved there under the `storage_key` shown in its dataset record. The `download_url` field gives a direct link to the same file.

#### Why do some videos have empty technical details?

Fields such as codec or bitrate are measured from the saved file. If a value cannot be determined, it comes back as `null` while the file itself is still downloaded.

#### Can I download Stories or highlights?

No. Only posts, Reels and IGTV videos are supported.

#### How fresh is the data?

Every run reads the posts live, so files and details reflect each post as it is at the time of the run.

#### Is this affiliated with Instagram or Meta?

No. This is an independent tool and is not affiliated with, endorsed by or sponsored by Instagram or Meta Platforms, Inc.

# Actor input Schema

## `postUrls` (type: `array`):

Instagram post, reel or IGTV URLs to download. Accepts /p/, /reel/, /reels/, /tv/, and canonical username/p/shortcode formats. Duplicates are silently de-duplicated.

## `maxPosts_per_username` (type: `integer`):

Maximum number of posts to download per username (after the date and pinned-post filters below). Public profiles need no sign-in, however many posts you ask for; a private profile needs your cookies.

## `newerThan` (type: `string`):

Skip posts published before this date. Leave empty for no date filter. Must be a date from 2000-01-01 up to the day before the run. Applies to usernames only. The date is read from the post's id, so it can be off by up to a day.

## `skipPinnedPosts` (type: `boolean`):

Leave out the posts a profile has pinned to the top of its grid. Applies to usernames only. A pinned post that is also the newest post cannot be recognised and stays in.

## `usernames` (type: `array`):

Usernames whose recent posts should be processed. Each entry can be a username (with or without @) or a profile link such as https://www.instagram.com/natgeo/.

## `cookies` (type: `string`):

Optional. Leave empty to download public posts and the posts of public profiles without signing in. Paste your own Instagram cookies in JSON format to also download age-restricted posts and the posts of private profiles your account follows. It is a secret input, so the Apify Console hides and encrypts it.

Format: \[{"name":"sessionid","value":"...","domain":".instagram.com"}, ...]

## Actor input object example

```json
{
  "postUrls": [
    "https://www.instagram.com/p/Dew5IIlNfMW/"
  ],
  "maxPosts_per_username": 2,
  "skipPinnedPosts": false,
  "usernames": [
    "espnfc",
    "@leomessi",
    "https://www.instagram.com/natgeo/"
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset containing downloaded media metadata (filename, post URL, username, type, download status, and media dimensions)

## `mediaFiles` (type: `string`):

Key-value store containing the actual downloaded image and video files

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "postUrls": [
        "https://www.instagram.com/p/Dew5IIlNfMW/"
    ],
    "maxPosts_per_username": 0,
    "usernames": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/instagram-downloader-api-reels-video-and-photo").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "postUrls": ["https://www.instagram.com/p/Dew5IIlNfMW/"],
    "maxPosts_per_username": 0,
    "usernames": [],
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/instagram-downloader-api-reels-video-and-photo").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "postUrls": [
    "https://www.instagram.com/p/Dew5IIlNfMW/"
  ],
  "maxPosts_per_username": 0,
  "usernames": []
}' |
apify call dataspryva/instagram-downloader-api-reels-video-and-photo --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/instagram-downloader-api-reels-video-and-photo"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/QmBZX9qSfLXgafxuu/builds/Z7uGNw1jeH2kjwpMa/openapi.json
