Searching npm for an Instagram scraper in JavaScript turns up an uncomfortable pattern: projects built around undocumented endpoints stop receiving updates, browser scripts need frequent selector changes, and login-based libraries break when Instagram changes its request flow. Before adopting a package, check its latest release, open issues, supported Node versions, and whether its examples still work.
For an application that needs structured JSON rather than browser screenshots, a REST API keeps your Node.js code small. This tutorial uses GramScraper, but the same client design—timeouts, explicit errors, retries, and schema validation—applies to any provider.
Set up the project
mkdir instagram-data-demo
cd instagram-data-demo
npm init -y
export GRAMSCRAPER_API_KEY="gs_your_api_key"Node.js 18+ includes fetch, so no HTTP dependency is required. Keep the key in a server environment variable; never ship it in client-side JavaScript.
Create a reusable Node.js API client
const BASE_URL = "https://gramscraper.com/api/instagram";
export async function instagramGet(path, params) {
const url = new URL(`${BASE_URL}${path}`);
for (const [key, value] of Object.entries(params)) {
if (value !== undefined) url.searchParams.set(key, value);
}
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.GRAMSCRAPER_API_KEY}`,
Accept: "application/json",
},
signal: AbortSignal.timeout(20_000),
});
const body = await response.json().catch(() => ({}));
if (!response.ok) {
const error = new Error(body.error || `HTTP ${response.status}`);
error.status = response.status;
throw error;
}
return body;
}Fetch an Instagram profile
import { instagramGet } from "./gramscraper.mjs";
const profile = await instagramGet(
"/v1/user/by/username",
{ username: "instagram" }
);
console.log({
username: profile.username,
name: profile.full_name,
biography: profile.biography,
followers: profile.follower_count,
posts: profile.media_count,
verified: profile.is_verified,
});The endpoint returns JSON fields directly. A representative response includes username, full_name, biography, follower_count, following_count, media_count, is_verified, is_private, and profile URLs. Fields may be absent when Instagram does not expose them publicly.
Fetch posts and paginate
The media feed endpoint accepts a numeric user ID. Read the ID returned by the profile lookup, then request a page:
const page = await instagramGet(
"/v1/user/medias/chunk",
{ user_id: String(profile.pk || profile.id) }
);
for (const item of page.items || []) {
console.log(item.code, item.caption_text || "");
}
// The exact cursor field follows the endpoint response.
// Pass its value as end_cursor on the next request.
const nextCursor = page.next_end_cursor || page.end_cursor;
if (nextCursor) {
const nextPage = await instagramGet("/v1/user/medias/chunk", {
user_id: String(profile.pk || profile.id),
end_cursor: nextCursor,
});
console.log(nextPage.items?.length || 0);
}Inspect a live response before locking your data model. Upstream fields and cursor containers can vary by endpoint. Validate the subset your application requires and preserve the raw response when debugging.
Retry only temporary failures
Do not retry invalid keys, missing parameters, or exhausted credits in a loop. Retry 429 and selected 5xx responses only with bounded exponential backoff and jitter. Make jobs idempotent so a retry does not duplicate stored records.
Why a REST API is easier to maintain than a Node scraper
A direct scraper owns cookies, browser fingerprints, proxy behavior, internal endpoint discovery, HTML or JSON parsing, and every upstream change. An API client owns a documented URL, authentication, response validation, and normal HTTP resilience. The API can still experience limits or temporary failures, but Instagram-specific maintenance no longer lives inside your app.
GramScraper includes 100 free credits. Standard is $29 for 15,400 credits and Business is $300 for 320,000. Purchased credits do not expire. Review exact endpoint credit costs in the documentation.
Run your first Node.js lookup
Start with a profile response, then add only the endpoints your application needs.
Get 100 free credits →