How to Scrape Instagram Channel Reels and Posts in 2026
Need to pull every post or reel from an Instagram profile programmatically? Whether you're building an influencer analytics platform, monitoring competitor reels, or feeding training data into an AI model, scraping Instagram channels reliably in 2026 has become harder than it used to be.
In this guide, we'll walk through the two cleanest paths — one for the full posts feed (images, videos, carousels, reels) and one for reels-only — and explain how to handle pagination, rate limits, and the gotchas Instagram has added over the past year.
What Changed in 2026
Instagram's web feed used to be a simple HTML scrape. As of early 2026, three things have shifted:
- Logged-out profile pages serve a "soft error" SSR shell. The HTML loads with HTTP 200 but contains zero post data — only a
pageID: "httpErrorPage"flag and a generic shell. Old scrapers that parsedwindow._sharedDatareturned empty arrays. /api/v1/users/web_profile_info/blanket-rate-limits datacenter IPs. Both well-known commercial proxies and self-hosted scrapers get HTTP 429 on this endpoint, and it no longer matters which exit IP you rotate to.- Search and explore endpoints require an authenticated session. Keyword search at
/explore/search/keyword/redirects to/accounts/login/even with residential IPs.
The good news: the per-profile feed and clips endpoints (/api/v1/feed/user/<id>/ and /api/v1/clips/user/) still work for public profiles when you bootstrap a real LSD token through a proper session. That's what the SocialKit Instagram Channel Posts API and Channel Reels API do for you under the hood.
What You Can Extract
Both APIs return a normalized item shape so you don't have to branch by media type:
- Identifiers —
id,shortcode, canonical URL - Type —
image,video,carousel, orreel - Engagement —
likes,comments,views,plays - Content — full
caption,duration,width,height,timestamp - Media URLs —
thumbnailUrland directvideoUrl(signed CDN links) - Author — embedded
authorobject (id, username, full name, verification, profile pic) - Pagination —
cursortoken +hasMoreboolean
The Easy Way: Two Endpoints, One Pattern
Rather than building a Puppeteer + residential-proxy + cookie-rotation pipeline yourself, the Instagram Channel Posts API and Instagram Channel Reels API handle all of that internally. Same auth, same pagination model, same response shape.
Posts vs Reels — When to Use Which
- Use
/instagram/channel-postswhen you want the full feed in chronological order: images, carousels, video posts, and reels mixed together exactly the way Instagram displays them. - Use
/instagram/channel-reelswhen you only care about short-form video. The response excludes static posts and carousels, so every item has a real view count and a video URL.
Both endpoints take the same query parameters (url, limit, cursor) and return the same item shape — pick whichever matches your use case.
Endpoints
# Full posts feed
https://api.socialkit.dev/instagram/channel-posts
# Reels-only feed
https://api.socialkit.dev/instagram/channel-reels
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Instagram profile URL (e.g., https://www.instagram.com/natgeo) |
access_key | string | Yes | Your SocialKit access key |
limit | number | No | Items per request, 1–100 (default 12) |
cursor | string | No | Pagination cursor from a previous response |
Example Request — Posts
GET https://api.socialkit.dev/instagram/channel-posts?access_key=<your-access-key>&url=https://www.instagram.com/natgeo&limit=12
Sample Response
{
"success": true,
"data": {
"profileUrl": "https://www.instagram.com/natgeo",
"username": "natgeo",
"items": [
{
"id": "3890548995286221889",
"shortcode": "DX-AbK9ScRB",
"url": "https://www.instagram.com/p/DX-AbK9ScRB/",
"type": "reel",
"isVideo": true,
"caption": "One trip to Italy is never enough...",
"likes": 104690,
"comments": 1438,
"views": 4813905,
"plays": 4813905,
"duration": 73.32,
"timestamp": 1778009597,
"thumbnailUrl": "https://scontent-iad3-1.cdninstagram.com/...",
"videoUrl": "https://scontent-iad3-1.cdninstagram.com/...",
"width": 1216,
"height": 2160,
"productType": "clips",
"author": {
"id": "787132",
"username": "natgeo",
"fullName": "National Geographic",
"isVerified": true,
"profilePicUrl": "https://scontent-iad3-1.cdninstagram.com/..."
}
}
],
"count": 12,
"hasMore": true,
"cursor": "3889718998899684009_25025320"
}
}
Example Request — Reels
GET https://api.socialkit.dev/instagram/channel-reels?access_key=<your-access-key>&url=https://www.instagram.com/nasa&limit=12
The response shape is identical — only the items differ. Every entry has type: "reel", a real view count, and a video URL.
Code Examples
JavaScript / Node.js
const axios = require('axios');
async function getInstagramFeed(profileUrl, kind, accessKey, opts = {}) {
const endpoint = kind === 'reels' ? 'channel-reels' : 'channel-posts';
const response = await axios.get(`https://api.socialkit.dev/instagram/${endpoint}`, {
params: {
access_key: accessKey,
url: profileUrl,
limit: opts.limit || 50,
...(opts.cursor ? { cursor: opts.cursor } : {}),
},
});
return response.data.data;
}
// Walk an entire profile's reels feed with pagination
async function walkAllReels(profileUrl, accessKey) {
const all = [];
let cursor = null;
let page = 0;
while (true) {
const data = await getInstagramFeed(profileUrl, 'reels', accessKey, {
limit: 50,
cursor,
});
all.push(...data.items);
page += 1;
console.log(`Page ${page}: ${data.items.length} reels (running total: ${all.length})`);
if (!data.hasMore || !data.cursor) break;
cursor = data.cursor;
}
return all;
}
walkAllReels('https://www.instagram.com/nasa', 'your-access-key').then((reels) => {
console.log(`Pulled ${reels.length} total reels`);
const top = [...reels].sort((a, b) => b.views - a.views).slice(0, 5);
top.forEach((r) =>
console.log(`${r.views.toLocaleString()} views — ${r.url}`),
);
});
Python
import requests
def get_instagram_feed(profile_url, kind, access_key, limit=50, cursor=None):
endpoint = 'channel-reels' if kind == 'reels' else 'channel-posts'
params = {
'access_key': access_key,
'url': profile_url,
'limit': limit,
}
if cursor:
params['cursor'] = cursor
response = requests.get(
f'https://api.socialkit.dev/instagram/{endpoint}',
params=params,
timeout=30,
)
response.raise_for_status()
return response.json()['data']
def walk_all_posts(profile_url, access_key):
items = []
cursor = None
while True:
data = get_instagram_feed(profile_url, 'posts', access_key, limit=50, cursor=cursor)
items.extend(data['items'])
if not data['hasMore'] or not data.get('cursor'):
break
cursor = data['cursor']
return items
posts = walk_all_posts('https://www.instagram.com/natgeo', 'your-access-key')
print(f'Total posts pulled: {len(posts)}')
cURL
# First page
curl "https://api.socialkit.dev/instagram/channel-posts?access_key=YOUR_KEY&url=https://www.instagram.com/natgeo&limit=12"
# Next page using cursor from previous response
curl "https://api.socialkit.dev/instagram/channel-posts?access_key=YOUR_KEY&url=https://www.instagram.com/natgeo&limit=12&cursor=3889718998899684009_25025320"
Use Cases
- Influencer Vetting — Pull a creator's last 50 reels to verify the views-to-followers ratio before paying for a campaign. Bot-inflated accounts have followers but flat reel views.
- Competitor Tracking — Run the posts endpoint nightly against 30 competitor accounts and surface new posts above a view threshold into a Slack channel.
- Hook & Caption Mining — Sort reels by views, pull the top 1% across 50 creators in a niche, and feed the captions into an LLM to extract winning hook patterns.
- Brand-Affiliate Compliance — Monitor every post your affiliates publish for branded mentions or off-brand content.
- AI Training Data — Build labeled datasets of public Instagram reels with view counts as a quality signal for training video-understanding models.
Pagination — How It Works
Both endpoints return up to 100 items per request along with a cursor field. To get the next page, pass that cursor value back as a query parameter on your next request:
// Page 1
const page1 = await getInstagramFeed(url, 'posts', key, { limit: 50 });
// Page 2
const page2 = await getInstagramFeed(url, 'posts', key, { limit: 50, cursor: page1.cursor });
// ...keep going while data.hasMore === true
Cursors are opaque strings — don't try to parse them or build them yourself. They contain timing information that lets Instagram return consistent results even as the profile publishes new content during your pagination session.
When you've reached the end of the feed, hasMore flips to false and cursor becomes null.
Things to Watch Out For
Signed video URLs expire
The videoUrl and thumbnailUrl fields are signed Instagram CDN links. They expire after several hours. If you need to persist a video file, fetch the bytes immediately after the API call and store them in your own storage — don't store the signed URL itself.
Empty feeds for private accounts
Private accounts return count: 0 and hasMore: false. There's no workaround through the public API — you'd need to be a follower with login credentials, which neither endpoint supports.
View counts only on videos
Static images and carousels without video return views: 0. Use views > 0 to filter for video-style engagement, or check type === "reel" || type === "video".
Rate limits
Each request costs 1 credit per 20 results returned. A limit=12 call costs 1 credit. A limit=50 call costs 3 credits. Build credit budgeting into your bulk loops so you don't burn your monthly quota on a single profile with thousands of posts.
First call per username is slower
The first call for a previously-unseen username triggers a one-time profile resolution behind the scenes (~5-10 seconds). Subsequent calls — including paginated calls within the same session — return in ~1 second. If you're testing latency, run the same username twice.
Try It Free
You don't need to sign up to see the response shape. Use the Instagram Channel Posts Extractor and Instagram Channel Reels Extractor tools to pull a sample feed, inspect the JSON, and confirm the data matches what you need before integrating.
When you're ready to build, sign up for SocialKit to get 20 free credits — enough to pull ~400 posts or reels — without a credit card.
Conclusion
Scraping Instagram in 2026 means accepting that the easy paths (HTML scrapes, web_profile_info) are dead, and routing around them with proper session bootstrapping and the modern feed/clips endpoints. The Channel Posts API and Channel Reels API give you a one-call interface to do exactly that, with cursor pagination, normalized response shapes, and reasonable per-credit pricing.
Pick the right endpoint for your use case (posts for full feeds, reels for short-form), follow the cursor pagination pattern, and you can build influencer dashboards, content audit tools, or AI training pipelines without ever touching a Puppeteer cluster.
Ready to start? Check the Instagram Channel Posts API docs and Instagram Channel Reels API docs for full reference, or jump straight to the free extractor tools to see real responses against any public account.