
TikTok Shop Scraper
scrape.ly/store/shelfbright/tiktok-shop-scraperE-commerceSocial media
1 monthly user

Scrape public Instagram reels from a profile or reel URL. Get captions, play and like counts, comment counts, video URLs, durations, and timestamps for every reel.
Public Instagram reels, as clean dataset rows, from a username or a reel URL.
Built for influencer research, campaign checks, and competitive monitoring. Paste a profile, a numeric id, or a reel link, and each public reel comes back with the caption, timing, engagement, and media fields Instagram actually sent.
username accepts usernames, @handles, profile URLs, numeric profile ids, reel URLs, and share URLs. Each entry is scraped on its own.resultsLimit stops a profile feed (default 20). A direct reel URL ignores that cap and returns that reel.onlyPostsNewerThan keeps reels from a UTC date, an ISO timestamp, or a relative window such as 2 weeks. Pinned reels stay in the results unless you skip them.skipPinnedPosts drops reels Instagram marks as pinned, including older pins that would otherwise sit at the top of the feed.skipTrialReels drops a reel only when the payload itself marks it as a trial. Unmarked reels stay.likesCount is -1 when Instagram has disabled like and view counts. A real 0 stays 0.includeSharesCount adds sharesCount from the share or reshare count on the payload. reshareCount is written whenever Instagram sent it.includeDownloadedVideo stores the video in the run key-value store after the file contains an ftyp box, then sets downloadedVideo to that record. A failed download keeps the reel and omits the field.Request-based scraping keeps the run on HTTP. There is no browser to start, so a profile list spends its time on Instagram’s public reel data instead of on a second session.
A private profile, a post that is not a reel, or a request that fails is a single dataset row with error and errorDescription. The rest of the list still finishes. A username or id Instagram does not publish is different: the run fails, and that name is not stored as a row. No profile is filled in for it.
Schedule it. Point it at the accounts you watch, set a newer-than window, and export JSON or CSV from the dataset. Missing values are left off the item, so a column appears because Instagram sent it, not because the actor filled one in.
Runs on Scrapely use the residential proxy group. Logged-out only: no session cookie and no account.
{
"resultsLimit": 20,
"username": [
"natgeo"
]
}
One item from a real run of the example input, shortened. Long text is cut and lists show their first entries.
{
"id": "4005771580272580688",
"type": "Video",
"shortCode": "DeXXAddgJhQ",
"caption": "It’s snack time for these humpback whales. They use rings of bubbles like nets to concentrate fish and krill before surging through for a feast.\n\n#EuropeFrom…",
"hashtags": [
"EuropeFromAbove"
],
"mentions": [
"DisneyPlus"
],
"url": "https://www.instagram.com/p/DeXXAddgJhQ/",
"commentsCount": 170,
"firstComment": "😍",
"latestComments": [
{
"id": "17918751165452783",
"text": "😍",
"ownerUsername": "viajar_bien_2027",
"ownerProfilePicUrl": "https://scontent-jnb2-1.cdninstagram.com/…",
"timestamp": "2026-10-12T21:48:31.000Z",
"likesCount": 0,
"owner": {
"id": "…",
"username": "…",
"is_verified": "…"
}
},
{
"id": "18199907509378238",
"text": "Amazingly beautiful ❤️. Thanks for sharing this beautiful video clip 🙏. Rare moments of the Sea Giants 🐋🐋🐋🌊🌊.... Impressive 🤩",
"ownerUsername": "dilmijayasuriya",
"ownerProfilePicUrl": "https://scontent-jnb2-1.cdninstagram.com/…",
"timestamp": "2026-10-12T21:18:27.000Z",
"likesCount": 1,
"owner": {
"id": "…",
"username": "…",
"is_verified": "…"
}
}
],
"dimensionsHeight": 1920,
"dimensionsWidth": 1080,
"originalHeight": 1920,
"originalWidth": 1080,
"displayUrl": "https://scontent-jnb2-1.cdninstagram.com/…",
"videoUrl": "https://instagram.fmvd3-1.fna.fbcdn.net/…",
"alt": "Video by National Geographic on October 11, 2026. May be an image of grey whale, humpback whale, whale and text.",
"likesCount": 76931,
"videoPlayCount": 1501380,
"igPlayCount": 1501380,
"timestamp": "2026-10-11T19:01:22.000Z",
"ownerFullName": "National Geographic",
"ownerUsername": "natgeo",
"ownerId": "787132",
"productType": "clips"
}
| Field | Required | What it does |
|---|---|---|
username | Yes | Usernames, profile URLs, numeric ids, reel URLs, or share URLs. Each item is processed on its own. |
resultsLimit | No | Maximum reels per profile. Default 20. Ignored for a direct reel URL. |
onlyPostsNewerThan | No | UTC YYYY-MM-DD, an ISO timestamp, or a relative window (1 day, 2 weeks, 3 months). |
skipPinnedPosts | No | Drop pinned reels. Default false. |
skipTrialReels | No | Drop reels the payload marks as trial. Default false. |
includeSharesCount | No | Add sharesCount when the payload has a share or reshare count. Default false. |
includeDownloadedVideo | No | Save the MP4 and set downloadedVideo. Default false. |
{
"username": ["natgeo"],
"resultsLimit": 20
}
Each public reel is one dataset item. A field is omitted when Instagram did not send it. A real false or 0 is kept.
Identity and text: id, pk, type, shortCode, url, inputUrl, caption, hashtags, mentions, alt, productType, timestamp.
Counts: likesCount, commentsCount, videoViewCount, videoPlayCount, igPlayCount, fbLikeCount, fbPlayCount, reshareCount. sharesCount only when includeSharesCount is on and a count was present.
Media: displayUrl, videoUrl, audioUrl, videoDuration, dimensionsWidth, dimensionsHeight. originalWidth and originalHeight only when Instagram sent the original size. images and childPosts for carousel children. downloadedVideo only after a successful MP4 store.
People and context: ownerUsername, ownerId, ownerFullName, taggedUsers, sponsors, coauthorProducers, musicInfo, locationName, locationId, paidPartnership, affiliate, isPinned, isCommentsDisabled. facebookPage and threadsNetProfile only when the owner object includes them.
Comments: firstComment and latestComments (up to 10 preview comments, with id, text, ownerUsername, ownerProfilePicUrl, timestamp, likesCount, and owner).
A share URL that redirects records redirectedFromUrl.
A target that cannot be scraped, other than a username or id Instagram does not publish, is { "inputUrl", "error", "errorDescription" }. errorDescription is a status and code, or an exception type name. An unpublished username stores nothing and fails the run.
Pay per result, plus platform usage (compute and proxy traffic) at the normal rates.
| Event | When it is charged | Price |
|---|---|---|
| Reel | Each reel saved to the dataset | $0.0017 ($1.70 per 1,000 reels) |
| Video download | Each reel whose MP4 was saved, only when Include downloaded video is on | $0.012 |
Example: 1,000 reels ≈ $1.70 in event fees plus platform usage. A 20-reel run of the prefilled profile used about $0.013 of platform usage in our tests. Downloading videos also adds the proxy traffic of each MP4 (about 13 MB per reel in our tests). The shares count option is free. Error rows (private profile, unsupported URL) are never charged. If you set a maximum charge for the run, the scraper stops cleanly when it is reached and keeps every reel it already saved.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pytest
python -m src
pytest checks mapping and URL parsing. A full scrape runs on Scrapely, where the residential proxy is available.
Every field below can be set in the run form, passed through the API, or saved as a task. Unset fields fall back to the defaults shown.
usernamearrayRequiredInstagram username, profile URL, ID, or reel URL
Add Instagram usernames, profile URLs, IDs, or direct reel URLs. Each item will be processed individually.
resultsLimitintegerOptionaldefault 20Maximum reels per profile
Set how many reels should be scraped per profile. For example, if set to 5, the scraper will extract up to 5 reels from each profile. This setting is ignored when scraping direct reel URLs.
Narrow down which reels are returned.
onlyPostsNewerThanstringOptionalScrape reels newer than
Scrape reels from the provided date up to the present. Supported formats include `YYYY-MM-DD`, full ISO timestamps (e.g. `2025-09-23T10:02:01`), or relative time such as `1 day`, `2 weeks`, or `3 months`. All times are evaluated in UTC.
skipPinnedPostsbooleanOptionaldefault falseSkip pinned reels
Enable this option if you want to exclude pinned reels from the overall results.
skipTrialReelsbooleanOptionaldefault falseSkip trial reels
Leave out trial reels, which creators use to test a video with non-followers before sharing it with their audience.
includeSharesCountbooleanOptionaldefault falseInclude shares count
Add the number of shares for each reel.
includeDownloadedVideobooleanOptionaldefault falseInclude downloaded video
Download each reel video to the run's key-value store, so you keep a copy after Instagram's links expire.
Every run writes its results to a dataset. Preview it in the dashboard, then export it or read it through the API.
| Field | Type | Description |
|---|---|---|
| id | string | number | null | Unique identifier of the post |
| type | string | null | Type of the post (Image, Video, Sidecar, etc.) |
| shortCode | string | null | Short code used in the post URL |
| caption | string | null | Caption text of the post |
| hashtags | array | null | Hashtags extracted from the post caption |
| mentions | array | null | User mentions extracted from the post caption |
| url | string | null | URL of the Instagram post |
| commentsCount | number | null | Number of comments on the post |
| firstComment | string | null | Text of the first comment on the post |
| latestComments | array | null | Array of the most recent comments on the post |
| dimensionsHeight | number | null | Height of the post image or video in pixels |
| dimensionsWidth | number | null | Width of the post image or video in pixels |
| originalHeight | number | null | Height of the originally uploaded photo in pixels, before Instagram's resize. Instagram serves images at most 1080px wide, so this may be larger than dimensionsHeight. |
| originalWidth | number | null | Width of the originally uploaded photo in pixels, before Instagram's resize. Instagram serves images at most 1080px wide, so this may be larger than dimensionsWidth. |
| displayUrl | string | null | URL of the post thumbnail image |
| images | array | null | URLs of all images in the post (carousel posts have multiple) |
| videoUrl | string | null | URL of the post video file |
| audioUrl | string | null | URL of the audio stream for reel posts with separate audio |
| alt | string | null | Accessibility caption (alt text) of the post image |
| likesCount | number | null | Number of likes on the post |
| videoViewCount | number | null | Number of views for video posts. Deprecated by Instagram; may be null or stale. Prefer videoPlayCount. |
| videoPlayCount | number | null | Number of plays for video posts |
| igPlayCount | number | null | Instagram-specific play count for video posts |
| fbLikeCount | number | null | Number of Facebook likes on the post |
| fbPlayCount | number | null | Number of Facebook plays for video posts |
| reshareCount | number | null | Number of times the post was reshared |
| timestamp | string | null | ISO 8601 timestamp when the post was published |
| childPosts | array | null | Child posts in a carousel (sidecar) post |
| ownerUsername | string | null | Username of the post author |
| ownerId | string | number | null | Instagram user ID of the post author |
| ownerFullName | string | null | Full display name of the post author |
| isPinned | boolean | null | Whether the post is pinned to the top of the profile |
| productType | string | null | Product type of the post (e.g., feed, clips, igtv) |
| videoDuration | number | null | Duration of the video in seconds |
| paidPartnership | boolean | null | Whether the post is a paid partnership |
| affiliate | boolean | null | Whether the post contains affiliate content |
| sponsors | array | null | Sponsors tagged in the paid partnership post |
| taggedUsers | array | null | Users tagged in the post by the author |
| musicInfo | object | null | Music or audio attribution information for the post |
| coauthorProducers | array | null | Co-author producers credited on the post |
| locationName | string | null | Name of the location tagged in the post |
| locationId | string | number | null | ID of the location tagged in the post |
| pk | string | null | Primary key of the post from the Instagram API |
| redirectedFromUrl | string | null | Original URL that redirected to this post |
| isCommentsDisabled | boolean | null | Whether comments are disabled on the post |
| inputUrl | string | null | Input URL that was used to find this post |
| searchTerm | string | null | Search term used to find this post |
| searchSource | string | null | Source of the search that found this post |
| facebookPage | object | null | Facebook page linked to the post author |
| threadsNetProfile | object | null | Threads.net profile linked to the post author |
| metaData | object | null | Additional metadata from the parent profile (present when addParentData is enabled) |
| sharesCount | number | null | Number of shares/reposts of the reel (when includeSharesCount is enabled) |
| transcript | string | null | Transcript of the reel audio (when includeTranscript is enabled) |
| downloadedVideo | string | null | URL to the downloaded video file in the run's key-value store (when includeDownloadedVideo is enabled) |
| error | string | null | Error code if the post could not be fully scraped |
| errorDescription | string | null | Human-readable description of the error |
| requestErrorMessages | array | null | Array of error messages encountered during scraping |
You pay for the events the actor reports, such as each result or page it produces. Platform usage is included in the event prices.
| Event | Charged when | Price |
|---|---|---|
| Reel | One Instagram reel saved to the dataset | $0.0017 |
| Video download | One reel MP4 downloaded and stored (only when Include downloaded video is on) | $0.012 |
Run this actor from your own code. Swap in your API token and the examples work as-is.
import { ScrapelyClient } from 'scrapely-client'
const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN })
// Start the actor and wait for the run to finish.
const run = await client.actor('echofield/instagram-reel-scraper').call({
"username": [
"natgeo"
],
"resultsLimit": 20
})
// Read the dataset the run produced.
const { items } = await client.dataset(run.defaultDatasetId).listItems()
console.log(items)Tokens live in your account settings. Keep them server-side: a token carries the full permissions of your account.
Connect the Scrape.ly MCP server once and your agent can run this actor as a tool. Pick your tool:
Fetch scrape.ly/agents.md and run echofield/instagram-reel-scraperPaste into Claude Code, Codex, Cursor or any AI agent. It signs you in once in the browser.
{
"mcpServers": {
"scrapely": {
"url": "https://api.scrape.ly/mcp?tools=actors,runs,storage,docs&actors=echofield/instagram-reel-scraper"
}
}
}No key in the file: your client opens the browser to sign you in the first time it connects.
Inputs the developer prepared for common jobs. Open one to see its input, then copy it to your account to run it as-is or adjust it.
Latest reels of a public Instagram profile.
Reels including how often they were shared.
Latest reels, skipping pinned and trial reels.
Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.