# Instagram Media Metadata Scraper (`w3crawler/instagram-media-metadata-scraper`) Actor

Read public Instagram post and profile metadata, captions, author fields, media URLs, and structured data without downloading media bytes.

- **URL**: https://apify.com/w3crawler/instagram-media-metadata-scraper.md
- **Developed by:** [w3crawler](https://apify.com/w3crawler) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 media metadata

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Media Metadata Scraper

Read public Instagram post, reel, TV, and profile metadata from page HTML, Open Graph tags, embedded public JSON, JSON-LD, and the public oEmbed endpoint. Saved records can include captions, author and profile fields, canonical URLs, engagement counts, media references and dimensions, profile statistics, and links discovered on a profile. Media bytes are never downloaded.

### Get started

1. Open the [Actor Store page](https://apify.com/w3crawler/instagram-media-metadata-scraper) and select **Try for free** or open the **Input** tab.
2. Supply media URLs in `postUrls` (or the legacy `post_urls`) and/or profile `usernames`. At least one target is required.
3. Set `maxItems` and `maxPosts_per_username` to bound the run. Keep both small for the first run.
4. Start the Actor, open the dataset, and export the records as JSON, CSV, or Excel. Open **Run summary** (`OUTPUT`) to check record and failure counts.

### Input

At least one target is required at runtime, although the schema marks every field optional. Unsupported field names and out-of-range values are rejected before requests start.

| Field | Type | Required? | Default | Meaning |
| --- | --- | --- | --- | --- |
| `postUrls` | string array | One of `postUrls` / `post_urls` / `usernames` | Not supplied (prefill: one reel URL) | HTTPS Instagram post, reel, or TV URLs, 1–100 entries. Public page metadata is read; media bytes are not downloaded. |
| `post_urls` | string array | One of `postUrls` / `post_urls` / `usernames` | Not supplied | Legacy snake_case alias for `postUrls`. Both lists are combined and deduplicated. |
| `usernames` | string array | One of `postUrls` / `post_urls` / `usernames` | Not supplied (prefill: `instagram`) | Public usernames with an optional leading `@`. Each profile emits a profile record, and up to `maxPosts_per_username` observed media links are enriched within the remaining `maxItems`. |
| `maxPosts_per_username` | integer | Optional | `10` | Maximum media links discovered from each profile page, 1–100. Discovery is also capped by the remaining global target budget. |
| `maxItems` | integer | Optional | `100` | Global cap, 1–100, on unique scheduled targets and saved normal records across direct URLs and profile-discovered media. Diagnostics are logged, not written as rows. |
| `requestTimeoutSecs` | integer | Optional | `20` | Per-page request timeout, 5–60 seconds. oEmbed requests use the lower of this value and 30 seconds. |
| `maxRequestRetries` | integer | Optional | `2` | Retries for transient network and 429/5xx responses, 0–3. Access blocks are not retried indefinitely. |
| `requestDelayMs` | integer | Optional | `0` | Delay before each target after the first, 0–10,000 ms. |
| `proxyConfiguration` | object | Optional | `{"useApifyProxy":false}` | Optional Apify Proxy settings: `useApifyProxy`, `apifyProxyGroups`, and `apifyProxyCountry`. Direct requests are the default; proxy credentials are never written to output. |

Example input using the prefill and defaults:

```json
{
  "postUrls": [
    "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/"
  ],
  "usernames": ["instagram"],
  "maxPosts_per_username": 10,
  "maxItems": 100,
  "requestTimeoutSecs": 20,
  "maxRequestRetries": 2,
  "requestDelayMs": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

Remove either target list if it is not needed. Supplying both is valid; duplicate normalized targets are processed once.

### Output

The dataset contains one row per saved media or profile record. Optional values are omitted when Instagram does not expose them, and source failures produce log diagnostics instead of placeholder rows. Public pages can expose different fields for posts, reels, TV pages, and profiles.

| Field | Type | Availability | Meaning |
| --- | --- | --- | --- |
| `url` | string | Always | Normalized target URL; added by the dataset sanitizer. |
| `searchUrl` | string | Always | Compatibility alias equal to `sourceUrl`. |
| `id` | string | Always | Stable deterministic identifier derived from the normalized target URL. |
| `recordType` | string enum | Always | `instagram-media-metadata` or `instagram-profile-metadata`. |
| `targetType` | string enum | Always | `media` or `profile`. |
| `targetUrl` | string | Always | Normalized target URL requested or discovered from a profile. |
| `sourceUrl` | string | Always | Public Instagram page URL used for extraction. |
| `discoveredFromProfile` | string | When discovered from a profile | Profile URL that exposed the media link. |
| `source` | string | Always | `instagram-public-page` or `instagram-public-page-and-oembed`. |
| `canonicalUrl` | string | Always | Canonical URL from Open Graph or oEmbed, with the target URL as fallback. |
| `pageTitle` | string | When published | HTML or Open Graph page title. |
| `caption` | string | When published | Public caption or description. Instagram may shorten it in page metadata. |
| `title` | string | When published | Public media title, or the profile display name for profile rows. |
| `author` | string | When published | Public author or creator name. |
| `authorUsername` | string | When published | Public author username. |
| `authorUrl` | string | When published | Public author profile URL. |
| `authorId` | string or number | When published | Public author identifier from oEmbed or embedded data. |
| `profileUsername` | string | When published | Profile username for a profile target or the media owner. |
| `profileFullName` | string | When published | Public profile display name. |
| `profileBio` | string | When published | Public profile biography from embedded page data. |
| `profileFollowers` | integer | When published | Public follower count. |
| `profileFollowing` | integer | When published | Public following count. |
| `profilePostCount` | integer | When published | Public profile post count. |
| `profileIsVerified` | boolean | When published | Public verification flag. |
| `profileIsPrivate` | boolean | When published | Public private-profile flag. |
| `profileImageUrl` | string | When published | Profile image URL. |
| `profileExternalUrls` | string array | When published | Public external links exposed by the profile, capped at 20. |
| `highlightUrls` | string array | When published | Public profile highlight URLs, capped at 50. |
| `recentPostUrls` | string array | When published | Public media URLs observed on a profile, capped at 100. |
| `imageUrl` | string | When published | Primary image URL for the target media. |
| `mediaUrl` | string | When published | Primary public video URL for the target media. |
| `mediaType` | string enum | When published | Exposed media type: `image`, `video`, or `carousel`. This describes observed media references, so a reel URL can produce `image` when only an image is exposed. |
| `width` | integer | When published | Media width in pixels. |
| `height` | integer | When published | Media height in pixels. |
| `duration` | string | When published | Source duration representation. |
| `durationSeconds` | number | When published | Normalized media duration in seconds. |
| `uploadDate` | string | When published | Publication date/time from embedded data or page text. |
| `likeCount` | integer | When published | Public like count; display labels may be rounded or abbreviated. |
| `commentCount` | integer | When published | Public comment count; display labels may be rounded. |
| `videoViewCount` | integer | When published | Public video view or play count. |
| `carouselItemCount` | integer | When published | Number of carousel children. |
| `embedHtml` | string | When published | Public oEmbed HTML snippet. |
| `oembedProvider` | string | When published | oEmbed provider name. |
| `structuredData` | object array | When published | Up to 10 public JSON-LD objects embedded in the page. |
| `mediaReferences` | object array | When published | Deduplicated media references with `url`, `kind`, `width`, `height`, `durationSeconds`, and `source`, capped at 30. |
| `downloadAvailable` | boolean | Always | Whether a public media URL was observed. No bytes are stored or downloaded. |
| `transcriptAvailable` | boolean | Always | Whether a public transcript was observed. Current page extraction reports `false`. |
| `metadata` | object | When published | Small page subset: `contentLanguage` and `openGraphType`. |
| `scrapedAt` | string | Always | Time the row was emitted, in ISO UTC. |

Example of a real saved row from the 2026-10-25 run. The `mediaReferences` array contains its single real entry; no values are invented.

```json
{
  "id": "instagram:df697121c65cd31bbffda568",
  "recordType": "instagram-media-metadata",
  "targetType": "media",
  "targetUrl": "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/",
  "sourceUrl": "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/",
  "source": "instagram-public-page",
  "canonicalUrl": "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/",
  "pageTitle": "Instagram on Instagram: \"that’s his spa now 🚰 #InTheMoment Video by @misterbeanito\"",
  "caption": "that’s his spa now 🚰 #InTheMoment Video by @misterbeanito\".",
  "title": "Instagram on Instagram: \"that’s his spa now 🚰 #InTheMoment Video by @misterbeanito\"",
  "author": "Instagram",
  "authorUsername": "instagram",
  "imageUrl": "https://scontent-iad3-1.cdninstagram.com/v/t51.71878-15/795341832_1575427477361093_319242826385620795_n.jpg?stp=cmp1_dst-jpg_e35_s640x640_tt6&_nc_cat=108&ccb=7-5&_nc_sid=18de74&efg=eyJlZmdfdGFnIjoiQ0xJUFMuYmVzdF9pbWFnZV91cmxnZW4uQzIifQ%3D%3D&_nc_ohc=GwEZWrttYssQ7kNvwF_eWHA&_nc_oc=AdpXONfQnWz4X2sS_weAaiFcNEvOPiKbLk5NNEYI1h1OWChqpM4_Y5PDhepHmg55IFU&_nc_zt=23&_nc_ht=scontent-iad3-1.cdninstagram.com&_nc_gid=VtCqvTbex-3yOFn4wow_fQ&_nc_ss=70689&oh=00_AQMVUkQHRiKQRf3_1uzFjpLY_HQzVwgTHo5XI4vqx5x8Zg&oe=6AE4502C",
  "mediaType": "image",
  "uploadDate": "2026-09-04T00:00:00.000Z",
  "likeCount": 966000,
  "commentCount": 22000,
  "mediaReferences": [
    {
      "url": "https://scontent-iad3-1.cdninstagram.com/v/t51.71878-15/795341832_1575427477361093_319242826385620795_n.jpg?stp=cmp1_dst-jpg_e35_s640x640_tt6&_nc_cat=108&ccb=7-5&_nc_sid=18de74&efg=eyJlZmdfdGFnIjoiQ0xJUFMuYmVzdF9pbWFnZV91cmxnZW4uQzIifQ%3D%3D&_nc_ohc=GwEZWrttYssQ7kNvwF_eWHA&_nc_oc=AdpXONfQnWz4X2sS_weAaiFcNEvOPiKbLk5NNEYI1h1OWChqpM4_Y5PDhepHmg55IFU&_nc_zt=23&_nc_ht=scontent-iad3-1.cdninstagram.com&_nc_gid=VtCqvTbex-3yOFn4wow_fQ&_nc_ss=70689&oh=00_AQMVUkQHRiKQRf3_1uzFjpLY_HQzVwgTHo5XI4vqx5x8Zg&oe=6AE4502C",
      "kind": "image",
      "source": "open_graph"
    }
  ],
  "downloadAvailable": true,
  "transcriptAvailable": false,
  "metadata": {
    "openGraphType": "article"
  },
  "scrapedAt": "2026-10-25T20:47:35.308Z",
  "url": "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/",
  "searchUrl": "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/"
}
```

### Run summary

**Run summary** is stored separately from the dataset in the run's key-value store under `OUTPUT`.

| Field | Meaning |
| --- | --- |
| `status` | Actor process outcome: `SUCCEEDED` or `FAILED`. |
| `runStatus` | Canonical run status; matches `status`. |
| `dataStatus` | Completeness of saved rows: `COMPLETE`, `PARTIAL`, `NO_RESULTS`, or `FAILED`. |
| `requestedTargetCount` | Unique targets left after normalization and the `maxItems` cap. |
| `scheduledTargetCount` | Target pages attempted, including failed pages. |
| `emittedCount` | Normal records written to the dataset. |
| `normalCount` | Normal records written; matches `emittedCount`. |
| `diagnosticCount` | Source diagnostics logged; diagnostics are not dataset rows. |
| `failureCount` | Diagnostics other than `NO_PUBLIC_METADATA` and `PAGE_UNAVAILABLE`, plus dataset write failures. |
| `uniqueTargetCount` | Distinct target URLs seen in results, including failed targets. |
| `profileRecordCount` | Normal profile records produced before dataset-write failures. |
| `mediaRecordCount` | Normal media records produced before dataset-write failures. |
| `discoveredPostCount` | Media links discovered from profile pages and queued within the target budget. |
| `oembedRequests` | oEmbed endpoint attempts. A media target can try up to two endpoints. |
| `blockedCount` | Pages treated as blocked or challenge responses. |
| `maxItems` | Global target and record cap used for the run. |
| `datasetWriteFailureCount` | Records that could not be written to the dataset. |
| `optionalFailureCount` | Optional summary-write failures. |
| `completedAt` | Time the summary was written, in ISO UTC. |

| Status | Data status | Meaning |
| --- | --- | --- |
| `SUCCEEDED` | `COMPLETE` | Records were saved without diagnostics. |
| `SUCCEEDED` | `PARTIAL` | Records were saved, but at least one diagnostic or dataset write failure occurred. Saved rows remain available. |
| `SUCCEEDED` | `NO_RESULTS` | The run completed cleanly with no usable public metadata and no counted failures. |
| `FAILED` | `FAILED` | No record was saved and at least one counted failure occurred. |

Example `OUTPUT` from the 2026-10-25 run:

```json
{
  "status": "SUCCEEDED",
  "runStatus": "SUCCEEDED",
  "dataStatus": "COMPLETE",
  "requestedTargetCount": 1,
  "scheduledTargetCount": 1,
  "emittedCount": 1,
  "normalCount": 1,
  "diagnosticCount": 0,
  "failureCount": 0,
  "uniqueTargetCount": 1,
  "profileRecordCount": 0,
  "mediaRecordCount": 1,
  "discoveredPostCount": 0,
  "oembedRequests": 2,
  "blockedCount": 0,
  "maxItems": 1,
  "datasetWriteFailureCount": 0,
  "optionalFailureCount": 0,
  "completedAt": "2026-10-25T20:47:35.385Z"
}
```

### Coverage and limitations

The Actor reads only public page HTML, embedded public JSON, Open Graph metadata, JSON-LD, and Instagram's public oEmbed endpoints. It does not log in, use cookies or private APIs, solve challenges, bypass rate limits, download image or video bytes, fetch transcripts, or collect comments. `transcriptAvailable` currently reports `false`, and `downloadAvailable` only means a public media URL was observed; it does not grant reuse rights.

Profile targets produce one profile record plus up to `maxPosts_per_username` discovered media targets, all within the remaining `maxItems` budget. Discovered links are whatever the public page exposes; `recentPostUrls` is not a complete profile catalog. A reel URL can yield `mediaType: "image"` when Instagram exposes only a still image for that request. Media and CDN URLs are time-limited and can expire after the run. Engagement counts parsed from display text may be rounded. Captions may be shortened by the page metadata, and a blank field means the public response did not expose it.

`maxItems` limits normalized targets and saved rows; log diagnostics do not create rows. oEmbed is optional enrichment, so records without oEmbed fields are still valid. `NO_PUBLIC_METADATA`, `PAGE_UNAVAILABLE`, `BLOCKED_OR_CHALLENGE`, HTTP errors, and request failures are reported through logs and `OUTPUT`, and can leave the run `PARTIAL` or `FAILED`. Apify Proxy is optional; direct requests are the default.

### Usage and support

Cost depends on the number of unique targets, profile discovery, oEmbed attempts, page size, retries, and proxy use. Start with a single URL or profile and a low `maxItems`, inspect the dataset and `OUTPUT`, then scale. See the Actor's Pricing tab for current account rates.

Use the [Actor Issues page](https://apify.com/w3crawler/instagram-media-metadata-scraper/issues) to report a problem. Include the run ID, the input used, and the run summary; do not include tokens, cookies, or private data. This Actor is not affiliated with Instagram or Meta. Use it only for public data and in accordance with Instagram's terms, robots guidance, and applicable law. Public pages can contain personal information; use the output only for lawful purposes.

# Actor input Schema

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

HTTPS Instagram post, reel, or TV URLs. The Actor reads public page metadata and does not download media bytes.

## `post_urls` (type: `array`):

Legacy snake_case alias for postUrls. If both aliases are supplied, their values are processed together and deduplicated.

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

Public Instagram usernames. Each profile page is emitted as a profile metadata record and up to maxPosts_per_username observed media links are enriched.

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

Maximum number of public media links discovered from each profile page. This legacy name is preserved for compatibility.

## `maxItems` (type: `integer`):

Global limit on emitted normal and diagnostic records across direct URLs and profile-discovered media.

## `requestTimeoutSecs` (type: `integer`):

Timeout for each public page request; oEmbed requests use the lower of this value and 30 seconds.

## `maxRequestRetries` (type: `integer`):

Retries for transient network and 429/5xx responses. Access blocks are not retried indefinitely.

## `requestDelayMs` (type: `integer`):

Optional respectful delay before each target after the first.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy configuration. Direct requests are the default. Proxy credentials are resolved by Apify and never stored in output.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.instagram.com/instagram/reel/Dc30nJeRKKz/"
  ],
  "usernames": [
    "instagram"
  ],
  "maxPosts_per_username": 2,
  "maxItems": 5,
  "requestTimeoutSecs": 20,
  "maxRequestRetries": 1,
  "requestDelayMs": 500,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runSummary` (type: `string`):

JSON record written to the OUTPUT key-value record.

# 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/instagram/reel/Dc30nJeRKKz/"
    ],
    "usernames": [
        "instagram"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/instagram-media-metadata-scraper").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/instagram/reel/Dc30nJeRKKz/"],
    "usernames": ["instagram"],
}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/instagram-media-metadata-scraper").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/instagram/reel/Dc30nJeRKKz/"
  ],
  "usernames": [
    "instagram"
  ]
}' |
apify call w3crawler/instagram-media-metadata-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/instagram-media-metadata-scraper"
        }
    }
}
```

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/Ltabqcw1RrD4vXBDd/builds/jggvqaqhOUzk0HTpG/openapi.json
