# Instagram Highlights Scraper - Stories & Media (`khadinakbar/instagram-highlights-scraper`) Actor

Scrape Instagram story highlights from any public profile or highlight ID. Returns every story item with image/video URLs, timestamps, captions, video duration, and music, plus highlight titles and covers. No login or cookies. MCP-ready. $0.003 per story item.

- **URL**: https://apify.com/khadinakbar/instagram-highlights-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Social media, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 highlight story items

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Instagram Highlights Scraper

Scrape Instagram story highlights from any public profile or highlight ID. The actor returns every story item inside each highlight with image and video CDN URLs, timestamps, captions, video duration, and music metadata, plus the highlight title, cover, and owner context. It is provider-backed with ScrapeCreators first and SociaVault fallback, so it needs no Instagram login, cookies, browser, or proxy setup.

### What It Extracts

By default (`includeStories: true`) one dataset row is one story item, with the highlight and owner metadata denormalized on every row — so spreadsheets, BI tools, and AI agents can filter and join without a second lookup.

| Field | Description |
| --- | --- |
| `recordType` | `story` (default mode) or `highlight` (list-only mode) |
| `highlightId` | Instagram highlight reel ID |
| `highlightTitle` | Display title of the highlight tray |
| `highlightUrl` | Canonical URL of the highlight |
| `position` | 1-based position of the item inside the highlight |
| `mediaId` | Instagram media ID of the story item |
| `shortcode` | Public shortcode of the item |
| `storyUrl` | Canonical Instagram story URL |
| `mediaType` | `video` or `image` |
| `imageUrl` / `thumbnailUrl` | Image CDN URLs (full and thumbnail) |
| `videoUrl` | Progressive MP4 CDN URL for video items |
| `videoDuration` | Video length in seconds |
| `hasAudio` | Whether the video carries audio |
| `width` / `height` | Original media dimensions in pixels |
| `caption` | Caption text, when present |
| `likeCount` / `commentCount` | Aggregated counts, when the provider exposes them |
| `isPaidPartnership` | Paid-partnership marker |
| `musicArtist` / `musicTitle` | Attached music sticker metadata, when present |
| `takenAt` | ISO 8601 timestamp of the original post |
| `ownerId` / `ownerUsername` / `ownerVerified` | Highlight owner identity |
| `coverImageUrl` | Highlight cover (highlight rows) |
| `provider` / `scrapedAt` / `sourceUrl` | Provenance fields |

### When To Use

This actor works well for:

- Archiving a brand's or competitor's permanent highlight content before it changes
- Competitive analysis of how accounts organize story marketing into highlights
- Collecting highlight media URLs for content libraries and creative research
- Auditing paid-partnership disclosures inside story highlights
- Feeding AI agents one clean highlight-item dataset from usernames or highlight IDs

For the 24-hour active stories tray, profile metadata, posts, reels, comments, hashtags, and follower lists, route to the matching Instagram actors in this portfolio (see Related Actors) — each is a focused standalone workflow for its own entity.

### Best Fit

Choose this actor when your starting point is a public profile handle (or a highlight link you already hold) and your deliverable is highlight content: tray titles, covers, and every story item with media URLs and timestamps. Start with `includeStories: false` to inventory trays cheaply, then feed the highlight IDs that matter into a second expanded run. Pair it with the [Instagram Profile Scraper](https://apify.com/khadinakbar/instagram-profile-scraper) when you also need follower counts and bio context for the same accounts.

### Input

```json
{
  "usernames": ["instagram"],
  "includeStories": true,
  "maxHighlightsPerUser": 20,
  "maxStoriesPerHighlight": 100,
  "maxTotalItems": 5000,
  "providerOrder": "scrapecreators-first"
}
```

You can also skip the profile listing entirely and pass direct highlight IDs or URLs:

```json
{
  "highlightIds": ["https://www.instagram.com/stories/highlights/18142207969557132/"]
}
```

#### Important Inputs

| Input | Default | Notes |
| --- | --- | --- |
| `usernames` | `["instagram"]` | Handles, @handles, or profile URLs |
| `highlightIds` | none | Highlight IDs or highlight URLs, with or without `usernames` |
| `includeStories` | `true` | Expand every story item; `false` saves one cheap row per highlight |
| `maxHighlightsPerUser` | `20` | Highlights processed per username (1–200) |
| `maxStoriesPerHighlight` | `100` | Story items saved per highlight (1–1000) |
| `maxTotalItems` | `5000` | Run-wide row cap and spend guard |
| `providerOrder` | `scrapecreators-first` | Provider routing; fallback is automatic |
| `includeRawData` | `false` | Attach raw provider payloads for debugging |

### Output Example

Real row from the `instagram` account's "creatives" highlight:

```json
{
  "recordType": "story",
  "highlightId": "18142207969557132",
  "highlightTitle": "creatives",
  "highlightUrl": "https://www.instagram.com/stories/highlights/18142207969557132/",
  "position": 1,
  "mediaId": "3811480328699137079",
  "shortcode": "DTlGSo0gEw3",
  "storyUrl": "https://www.instagram.com/stories/instagram/3811480328699137079/",
  "mediaType": "video",
  "imageUrl": "https://scontent-bos5-1.cdninstagram.com/v/t51.71878-15/616341681_1403647328019524_609409640201682429_n.jpg",
  "videoUrl": "https://scontent-bos5-1.cdninstagram.com/o1/v/t2/f2/m78/AQOd_6Ko6XVGRcbkl_piTFNNSHU2UxFWfFIjpdnK15GwiAeoa4EGsWVl869bbZ1oRLu0AwmWSNL0jEU8pK-CN8N13ZChU-BfJgFSRi4.mp4",
  "videoDuration": 24.333,
  "hasAudio": true,
  "width": 1080,
  "height": 1920,
  "takenAt": "2026-01-16T17:17:56.000Z",
  "ownerId": "25025320",
  "ownerUsername": "instagram",
  "ownerVerified": true,
  "provider": "scrapecreators",
  "scrapedAt": "2026-08-17T00:00:00.000Z",
  "sourceUrl": "https://www.instagram.com/instagram/"
}
```

### Pricing

This actor uses Pay per event (PPE) pricing with platform usage passed through.

| Event | Price |
| --- | ---: |
| Actor start | `$0.00005` |
| Highlight story item saved (`includeStories: true`) | `$0.003` |
| Highlight row saved (`includeStories: false`) | `$0.002` |

Only one result event applies to any given row: expanded runs charge per story item and never additionally per highlight; list-only runs charge per highlight row. See the Pricing tab on this actor's page for the live price table. Platform usage (compute) is billed in addition to event charges, and the run's opening status message shows the approximate maximum event cost from your input caps before work starts.

### Reliability

The actor routes every request through managed public-data providers:

1. ScrapeCreators for the primary highlights and highlight-detail routes.
2. SociaVault as automatic fallback when the primary errors, rate-limits, or returns nothing usable.

Every run writes `OUTPUT` and `RUN_SUMMARY` records with provider attempts, status codes, highlights expanded, rows saved, credits used, stop reason, and the estimated event cost, so partial results and empty profiles are distinguishable from provider outages.

### Workflow Story

A typical workflow: a social team tracks five competitor accounts. They first run the actor with `includeStories: false` across all five handles to inventory highlight trays cheaply, then re-run with `highlightIds` pinned to the two trays that matter and `maxStoriesPerHighlight` raised, feeding the media URLs into their creative asset library. Each run ends with a `RUN_SUMMARY` they can diff week over week to see which highlights were renamed, re-covered, or removed.

### API Example

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('khadinakbar/instagram-highlights-scraper').call({
  usernames: ['instagram'],
  includeStories: true,
  maxHighlightsPerUser: 10,
  maxStoriesPerHighlight: 50,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### MCP And AI Agents

This actor is exposed through Apify MCP as `khadinakbar/instagram-highlights-scraper`. Agent prompt card:

> Scrape Instagram story highlights for one or more public profiles or highlight IDs. Use it when you need highlight titles, covers, or every story item with media URLs and timestamps; route to a sibling Instagram actor for the active 24-hour stories tray, posts, reels, comments, or profiles. Returns one row per story item (`recordType: "story"`) or per highlight in list-only mode, plus a `RUN_SUMMARY` with provider telemetry and cost. Costs $0.003 per story item, $0.002 per highlight row, plus a $0.00005 start.

### Best Results Guidance

- Provide exact handles without spaces; profile URLs also work as input.
- Start with `includeStories: false` when you only need to discover highlight trays, then pass selected `highlightIds` to an expanded run.
- Cap spend with `maxTotalItems`; the status message shows the cost ceiling before rows are billed.
- Treat a profile with zero highlights as a valid empty result: confirm with `RUN_SUMMARY.usersWithZeroHighlights` before retrying.
- Schedule list-only passes weekly and expanded runs on demand to keep archival costs predictable.

### Builder's Note

I built this actor because highlights are the most stable part of Instagram's story surface — they outlive the 24-hour tray — yet each provider returns them through a different envelope: one ships an array, the other an index-keyed object. I found the normalizer had to flatten both into the same row shape before the dataset stayed schema-stable across provider fallbacks. In my testing, public profiles with zero highlights returned a clean `VALID_EMPTY` outcome, which keeps batch jobs honest when a brand simply has no trays.

### Limits

- Public profiles and public highlights only; no private accounts or login-gated data.
- Story items are returned exactly as Instagram exposes them; some fields (likes, music) are absent on some items.
- Very large highlight collections are bounded by `maxHighlightsPerUser` and `maxStoriesPerHighlight`.
- `maxTotalItems` caps billed rows per run; use it as a spend guard for batches.
- Media URLs are Instagram CDN links with expiry; download promptly if you need the files.

### Related Actors

- [Instagram Posts Scraper](https://apify.com/khadinakbar/instagram-posts-scraper) for profile posts and hashtag feeds.
- [Instagram Profile Scraper](https://apify.com/khadinakbar/instagram-profile-scraper) for profile metadata.
- [Instagram Reels Scraper](https://apify.com/khadinakbar/instagram-reels-scraper) for reels feeds.
- [Instagram User Scraper](https://apify.com/khadinakbar/instagram-user-scraper) when you need to resolve and verify profile identity first.
- [Instagram Trending Reels Scraper](https://apify.com/khadinakbar/instagram-trending-reels-scraper) for discovering trending reels before choosing which profiles to track.

### Legal

Use this actor only for public Instagram content you are allowed to access and process. You are responsible for complying with Instagram's Terms of Service, privacy laws, data protection rules, and any platform or jurisdiction-specific requirements. This actor does not access private accounts or login-gated content.

# Actor input Schema

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

Instagram usernames whose public story highlights should be scraped. Accepts plain handles (e.g. "instagram"), @handles (e.g. "@natgeo"), or full profile URLs (e.g. "https://www.instagram.com/instagram/"). Leave empty if you only pass highlightIds. NOT post, reel, or hashtag URLs.

## `highlightIds` (type: `array`):

Direct Instagram highlight IDs (e.g. "18142207969557132") or highlight URLs (e.g. "https://www.instagram.com/stories/highlights/18142207969557132/") whose story items should be scraped without listing a profile first. Combine with usernames or use alone. NOT a story, post, or profile URL.

## `includeStories` (type: `boolean`):

When true (default), each highlight is opened and one row is saved per story item with media URLs, timestamps, captions, and video metadata. When false, only the highlight list is saved (one row per highlight: title, cover, owner) at a lower per-row price. Turning this off is useful for cheap highlight discovery before deep-diving selected highlights.

## `maxHighlightsPerUser` (type: `integer`):

Upper bound on how many highlights are processed per username, newest-tray order first. Accepts 1-200; defaults to 20. Raises the run cost ceiling because each highlight costs one provider call. NOT a cap on story items — use maxStoriesPerHighlight for that.

## `maxStoriesPerHighlight` (type: `integer`):

Upper bound on story items saved per expanded highlight. Accepts 1-1000; defaults to 100. Most highlights contain 5-50 items. NOT a global cap — use maxTotalItems for that.

## `maxTotalItems` (type: `integer`):

Hard global cap on saved rows across all targets; the run stops gracefully when reached. Accepts 1-100000; defaults to 5000. Works as a spend guard together with the per-result event price. NOT a per-target limit.

## `providerOrder` (type: `string`):

Order in which the backing data providers are tried for every request. Defaults to scrapecreators-first; the fallback provider is used automatically when the primary fails. Use sociavault-first to invert the order or a -only value to pin one provider. Providers are owner-configured secrets; you never need an API key.

## `includeRawData` (type: `boolean`):

When true, each row also carries the unmodified provider item under a "raw" key for debugging or field-level analysis. Defaults to false because raw payloads roughly triple row size. NOT needed for normal scraping — every normalized field is already extracted.

## Actor input object example

```json
{
  "usernames": [
    "instagram",
    "@natgeo"
  ],
  "highlightIds": [
    "18142207969557132"
  ],
  "includeStories": true,
  "maxHighlightsPerUser": 10,
  "maxStoriesPerHighlight": 50,
  "maxTotalItems": 500,
  "providerOrder": "scrapecreators-first",
  "includeRawData": false
}
```

# Actor output Schema

## `storyItems` (type: `string`):

One row per story item (default) or per highlight (list-only mode), with media URLs, timestamps, captions, highlight context, and owner info.

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

Run diagnostics, provider telemetry, counts, stop reason, and estimated PPE cost.

## `output` (type: `string`):

Same summary under the OUTPUT key for stable agent and readback evidence.

# 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 = {
    "usernames": [
        "instagram"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/instagram-highlights-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 = { "usernames": ["instagram"] }

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/instagram-highlights-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 '{
  "usernames": [
    "instagram"
  ]
}' |
apify call khadinakbar/instagram-highlights-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/instagram-highlights-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/MgkaQUdK9F1m7ChzE/builds/1UHxq0g6RHoRacSOc/openapi.json
