How to Scrape Instagram Search Results in 2026
Need to programmatically search Instagram for reels matching a keyword or hashtag? Whether you're building a trend-discovery tool, monitoring brand mentions, or mining viral content for a content team, scraping Instagram search has become substantially harder over the past year — and outright impossible if you go about it the wrong way.
In this guide, we'll walk through what changed in 2026, why most "Instagram search APIs" out there fail today, and how to actually pull search results without an Instagram account using the SocialKit Reels Search API.
What Changed in 2026
Three things shifted that broke older Instagram search scrapers:
- Keyword search GraphQL is auth-gated. The
PolarisKeywordSearchExplorePageRelayQueryendpoint that powers the explore page now requiresxdt_viewer.user.id— i.e., a logged-in viewer. Anonymous calls return400: Incorrect Query. /explore/search/keyword/?q=redirects to/accounts/login/for any unauthenticated browser. The SERP page is gone for anonymous users.- Datacenter-IP rate limits got tighter on
/api/v1/web/search/topsearch/,/api/v1/fbsearch/web/top_serp/,/api/v1/users/search/, and friends. Most return429immediately or404after redirect.
The old playbook — bootstrap an anonymous LSD token, POST to a /api/v1/... REST endpoint, parse JSON — does not work for search in 2026.
What Still Works: /popular/<keyword>/
There's exactly one Instagram surface that returns reels for a keyword without auth: the public /popular/<keyword>/ page. When you navigate /explore/tags/<keyword>/, IG redirects you here. The page renders ~24 reels for the query, with full metadata (shortcodes, captions, view counts, authors, thumbnails) embedded in an inline RelayPrefetchedStreamCache script tag.
Two catches:
- Direct HTTP fetches return an "error shell" — IG only ships the real SSR payload to what it considers a real browser session.
axios.getvia Decodo or even Evomi residential gets a 200 response with zero reels in the HTML. - Only a real browser fingerprint unlocks the data. Headless Chrome through a datacenter proxy works fine; raw HTTP doesn't.
That's exactly what the SocialKit Reels Search API does internally: it drives a browser session against /popular/<keyword>/, parses the inline JSON, caches the result for 30 minutes, and returns clean structured reels to your code.
What You Can Extract
The Reels Search API returns up to 24 reels per query (12 per page across 2 pages — that's IG's hard cap on anonymous popular-search). Each reel includes:
- Identifiers —
id,shortcode, canonical URL - Engagement —
views,plays(likes/comments often0from the public render — fetch per-reel via Stats API for fresh values) - Content —
caption,duration,width,height,timestamp - Media —
thumbnailUrl(signed CDN link) - Author —
username,fullName,isVerified,profilePicUrl
The Easy Way
Rather than building the puppeteer + proxy + inline-JSON-parsing pipeline yourself, the Instagram Reels Search API handles all of it. One REST call, one credit, results in JSON.
API Endpoint
https://api.socialkit.dev/instagram/reels-search
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Keyword (cooking) or hashtag (#dogs, %23cats) |
access_key | string | Yes | Your SocialKit access key |
page | number | No | 1-indexed page (1 or 2). Default: 1 |
Example Request
GET https://api.socialkit.dev/instagram/reels-search?access_key=YOUR_KEY&query=cats&page=1
Sample Response
{
"success": true,
"data": {
"query": "cats",
"page": 1,
"items": [
{
"id": "DU6Fh3QEZ2d",
"shortcode": "DU6Fh3QEZ2d",
"url": "https://www.instagram.com/reel/DU6Fh3QEZ2d/",
"type": "reel",
"isVideo": true,
"caption": "Which one suits me best? \n\n#cats_of_instagram #foryou #kittenlife",
"views": 35828358,
"plays": 35828358,
"thumbnailUrl": "https://scontent.cdninstagram.com/v/t51.71878-15/...",
"author": {
"username": "gush.thecat",
"fullName": "Gush",
"isVerified": false,
"profilePicUrl": "https://scontent.cdninstagram.com/..."
}
}
],
"count": 12,
"hasMore": true
}
}
Code Examples
JavaScript / Node.js
const axios = require('axios');
async function searchInstagramReels(query, accessKey, page = 1) {
const response = await axios.get('https://api.socialkit.dev/instagram/reels-search', {
params: { access_key: accessKey, query, page },
});
return response.data.data;
}
// Get both pages of results (max 24 reels)
async function getAllReels(query, accessKey) {
const all = [];
for (let page = 1; page <= 2; page++) {
const data = await searchInstagramReels(query, accessKey, page);
all.push(...data.items);
if (!data.hasMore) break;
}
return all;
}
const reels = await getAllReels('cats', 'your-access-key');
console.log(`Got ${reels.length} reels`);
// Sort by views to find the top performers
reels
.sort((a, b) => b.views - a.views)
.slice(0, 5)
.forEach((r) => console.log(`${r.views.toLocaleString()} views — ${r.url}`));
Python
import requests
def search_instagram_reels(query, access_key, page=1):
response = requests.get(
'https://api.socialkit.dev/instagram/reels-search',
params={'access_key': access_key, 'query': query, 'page': page},
timeout=30,
)
response.raise_for_status()
return response.json()['data']
# Hashtag also works — pass "#dogs" or "%23dogs"
reels = []
for page in (1, 2):
data = search_instagram_reels('#dogs', 'your-access-key', page=page)
reels.extend(data['items'])
if not data['hasMore']:
break
print(f'Got {len(reels)} reels')
top = sorted(reels, key=lambda r: r['views'], reverse=True)[:5]
for r in top:
print(f"{r['views']:,} views — @{r['author']['username']}: {r['url']}")
cURL
# Page 1
curl "https://api.socialkit.dev/instagram/reels-search?access_key=YOUR_KEY&query=cooking&page=1"
# Page 2 (next 12 reels)
curl "https://api.socialkit.dev/instagram/reels-search?access_key=YOUR_KEY&query=cooking&page=2"
# Hashtag — # is automatically stripped
curl "https://api.socialkit.dev/instagram/reels-search?access_key=YOUR_KEY&query=%23dogs"
Hashtags vs Keywords
The API normalizes both into the same lookup. Pass any of these — they all return the same results:
cats#cats%23catsCats(case is normalized)cats(whitespace is stripped)
Internally we lowercase, strip the leading #, and remove spaces. Whatever Instagram considers the canonical hashtag URL.
Use Cases
- Trend Radar — Run a list of niche keywords on a schedule (cron + the API), surface any reel that crossed N views into a Slack channel.
- Creator Discovery — Scan the top reels for 50 niche keywords weekly, build a database of the unique creators that appear, alert when new ones break out.
- Hook Library — Pull captions from the top 24 reels per niche, feed them into an LLM to extract winning hook patterns.
- Brand Mention Monitoring — Search your brand name + competitor names daily, surface any new reel mentioning them.
- Competitive Intelligence — Monitor reels for "vs" / "compared to" / "alternative" patterns plus competitor product names.
Pagination & Limits
Each call returns 12 reels. Pass page=2 for the next 12. Instagram caps anonymous popular-search at ~24 reels per query, so page is bounded to 2.
If you need more depth per query, the workaround is to search related keywords. The popular-search render itself surfaces 30 related-keyword suggestions per query — those are good leads for adjacent searches that each return another 24 reels.
Results are cached for 30 minutes per query. A cold call takes ~10–15 seconds (browser render time); subsequent calls return in under 2 seconds.
Things to Watch Out For
Likes and comments often show as 0
Instagram's public popular-search render only ships play_count on every reel. like_count and comment_count are typically 0 from search results. To get fresh likes/comments per reel, fetch each reel via /instagram/stats?url=<reel-url>.
Thumbnail URLs expire
The thumbnailUrl field is a signed Instagram CDN link. URLs expire after several hours. If you persist results to your own DB, fetch the image bytes immediately after the API call and store them yourself — don't store the signed URL.
Cold path is slow
The first call for any query takes 10–15 seconds while we render Instagram's search page in a headless browser. After that, a 30-minute cache makes repeat calls return in under 2s. If you're building real-time UX, warm the cache by pre-running the most common queries.
Hashtags vs keywords return identical results
This isn't a bug — Instagram's popular-search treats #cats and cats as the same query under the hood. Don't waste credits on both forms of the same search.
Try It Free
You don't need to write any code to try the response shape. Use the Instagram Reels Search Extractor tool — paste a keyword or hashtag, get the top reels rendered in your browser, and inspect the JSON output before integrating.
When you're ready to build, sign up for SocialKit and get 20 free credits — enough to run 20 different searches without a credit card.
Conclusion
Scraping Instagram search in 2026 means accepting that the easy paths (anonymous REST endpoints, raw HTML scraping) are gone. The only remaining unauthenticated surface is /popular/<keyword>/, and even that requires a real browser fingerprint to unlock the SSR payload.
The Reels Search API gives you that surface as a clean REST call: pass a query, get up to 24 reels with view counts, captions, and authors, paginated and cached. No Instagram account, no proxy setup, no anti-bot headache.
Ready to start? Check the Instagram Reels Search API docs for full reference, or jump straight to the free search tool to try it against any keyword right now.