{"_id":"@aduptive/instagram-scraper","_rev":"4-93a82bb161c36a55ef8373489508e581","name":"@aduptive/instagram-scraper","dist-tags":{"latest":"2.1.1"},"versions":{"1.0.0":{"name":"@aduptive/instagram-scraper","version":"1.0.0","keywords":["instagram","scraper","typescript","social-media","data-collection","web-scraping","public-content","no-authentication","rate-limiting","mobile-first","media","video","carousel"],"author":{"name":"aduptive"},"license":"MIT","_id":"@aduptive/instagram-scraper@1.0.0","maintainers":[{"name":"aduptive","email":"aduptive@gmail.com"}],"homepage":"https://github.com/aduptive/instagram-scraper#readme","bugs":{"url":"https://github.com/aduptive/instagram-scraper/issues"},"dist":{"shasum":"99453b261517d8e642120a6df8078c688dde441a","tarball":"https://registry.npmjs.org/@aduptive/instagram-scraper/-/instagram-scraper-1.0.0.tgz","fileCount":18,"integrity":"sha512-ehBrQkmAUEGU2zci0myDSyrk7EJ2EKC1GA000HnDwlJ2cF07xFHT9uZqfAkAjmcnXu27YazhZUZDe4P+dpq+zQ==","signatures":[{"sig":"MEQCIFlpEaM3q9HPFIzS3nu8IBATnBMN2Twjb40e7LboZ+1XAiAhWrbYtfJ2hmXGwUMIRA62OPZABLBbGtHnFg/KVTFo6g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":36088},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"dc18611d09098fcc3a1349db1bb314bbd8e3887c","scripts":{"lint":"eslint . --ext .ts","test":"jest","build":"tsc","clean":"rimraf dist","debug":"ts-node debug.ts","prepare":"npm run clean && npm run build","version":"npm run format && git add -A src","preversion":"npm run lint","postversion":"git push && git push --tags","prepublishOnly":"npm test && npm run lint"},"_npmUser":{"name":"aduptive","email":"aduptive@gmail.com"},"repository":{"url":"git+https://github.com/aduptive/instagram-scraper.git","type":"git"},"_npmVersion":"10.9.0","description":"Modern TypeScript library for collecting public Instagram content with smart delays, mobile-first approach, and media support","directories":{},"_nodeVersion":"18.20.4","dependencies":{"axios":"^1.6.7","cheerio":"^1.0.0-rc.12"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","rimraf":"^5.0.5","ts-jest":"^29.2.5","prettier":"^3.2.1","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.0","@types/cheerio":"^0.22.35","@typescript-eslint/parser":"^6.18.1","@typescript-eslint/eslint-plugin":"^6.18.1"},"_npmOperationalInternal":{"tmp":"tmp/instagram-scraper_1.0.0_1730164949440_0.48212199725548754","host":"s3://npm-registry-packages"}},"1.0.3":{"name":"@aduptive/instagram-scraper","version":"1.0.3","keywords":["instagram","scraper","typescript","social-media","data-collection","web-scraping","public-content","no-authentication","rate-limiting","mobile-first","media","video","carousel"],"author":{"name":"aduptive"},"license":"MIT","_id":"@aduptive/instagram-scraper@1.0.3","maintainers":[{"name":"aduptive","email":"aduptive@gmail.com"}],"homepage":"https://github.com/aduptive/instagram-scraper#readme","bugs":{"url":"https://github.com/aduptive/instagram-scraper/issues"},"dist":{"shasum":"2719a9e6702cbb8409e8bab81783bc9c42c18d27","tarball":"https://registry.npmjs.org/@aduptive/instagram-scraper/-/instagram-scraper-1.0.3.tgz","fileCount":18,"integrity":"sha512-DcfNKB0QeeeTDVv005ObJqMSLquHY3YFCsd2n1cBZwAwmDB6rtyli7bA4tohUEdPmBecI1XLo92eOOFZBaJn1Q==","signatures":[{"sig":"MEUCIDNVU3SzdrtOaZjHffFz7ntESoIG736c4GR7OybgpBUBAiEA3XNtfUihXnlpzgW1pVPvRqkTCUPCoUKnp5ox1z7qSho=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":36138},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"cb7a1117a64f877f65f2830f7f95974ab9cf98b6","scripts":{"lint":"eslint . --ext .ts","test":"jest","build":"tsc","clean":"rimraf dist","debug":"ts-node debug.ts","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run clean && npm run build","version":"npm run format && git add -A src","preversion":"npm run lint","postversion":"git push && git push --tags","prepublishOnly":"npm test && npm run lint"},"_npmUser":{"name":"aduptive","email":"aduptive@gmail.com"},"repository":{"url":"git+https://github.com/aduptive/instagram-scraper.git","type":"git"},"_npmVersion":"10.9.0","description":"Modern TypeScript library for collecting public Instagram content with smart delays, mobile-first approach, and media support","directories":{},"_nodeVersion":"18.20.4","dependencies":{"axios":"^1.6.7","cheerio":"^1.0.0-rc.12"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","rimraf":"^5.0.5","ts-jest":"^29.2.5","prettier":"^3.2.1","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.0","@types/cheerio":"^0.22.35","@typescript-eslint/parser":"^6.18.1","@typescript-eslint/eslint-plugin":"^6.18.1"},"_npmOperationalInternal":{"tmp":"tmp/instagram-scraper_1.0.3_1734150534689_0.9911078851561221","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@aduptive/instagram-scraper","version":"2.0.0","keywords":["instagram","scraper","typescript","social-media","data-collection","web-scraping","public-content","no-authentication","rate-limiting","mobile-first","media","video","carousel"],"author":{"name":"aduptive"},"license":"MIT","_id":"@aduptive/instagram-scraper@2.0.0","maintainers":[{"name":"aduptive","email":"aduptive@gmail.com"}],"homepage":"https://github.com/aduptive/instagram-scraper#readme","bugs":{"url":"https://github.com/aduptive/instagram-scraper/issues"},"dist":{"shasum":"8c664f24e451ffd257ab423954c6ee7046258b23","tarball":"https://registry.npmjs.org/@aduptive/instagram-scraper/-/instagram-scraper-2.0.0.tgz","fileCount":18,"integrity":"sha512-1nvMeJE4lorSPqccb0V3FzBjzGV9I0FjlVyQvJGP/6GU9yFyx+NBpHI+Sjwee2rKxHSrmqAj6muW4oCv4zVqtw==","signatures":[{"sig":"MEUCICIDTGHa+ldInm0JLJBJkJTt2gJAMH1HJUVEN8DyCbRsAiEAoQr/1sWvEEjYUOErJlR8ogXxBFTFtGiCNBjfIzO0yyA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42819},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"09d17929fea70d65e5ee67bd4a680feacdbceb5d","scripts":{"lint":"eslint . --ext .ts","test":"jest","build":"tsc","clean":"rimraf dist","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run clean && npm run build","version":"npm run format && git add -A src","preversion":"npm run lint","postversion":"git push && git push --tags","prepublishOnly":"npm test && npm run lint"},"_npmUser":{"name":"aduptive","email":"aduptive@gmail.com"},"repository":{"url":"git+https://github.com/aduptive/instagram-scraper.git","type":"git"},"_npmVersion":"10.9.4","description":"Zero-dependency TypeScript library for scraping public Instagram profiles, posts and media without login, with built-in retries and rate limiting","directories":{},"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","rimraf":"^5.0.5","ts-jest":"^29.2.5","prettier":"^3.2.1","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.0","@typescript-eslint/parser":"^6.18.1","@typescript-eslint/eslint-plugin":"^6.18.1"},"_npmOperationalInternal":{"tmp":"tmp/instagram-scraper_2.0.0_1787604177770_0.42895967046455663","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"_id":"@aduptive/instagram-scraper@2.1.1","bugs":{"url":"https://github.com/aduptive/instagram-scraper/issues"},"dist":{"shasum":"88001cb342f18ed5920c8aac09367f198373b501","tarball":"https://registry.npmjs.org/@aduptive/instagram-scraper/-/instagram-scraper-2.1.1.tgz","fileCount":18,"integrity":"sha512-6noN+3Cj/dat0KqfrHr1aMJXwRiNlVIT33zYK4grxz9SZZj1yFmYpSGpZPgrs/kQb5jmXJqvEgVb07Zp8gO/pg==","signatures":[{"sig":"MEUCIQD3TmB+Ksp5DL56inCjbQ/WFwhVX3hnCGZgILNu24GFywIgP/vJ+bSsL0yxExgT6rA/AdEh56yUupm9gvEzggnZWl4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC4heynGvtJX4JjO8jly3HpUo/YzPwOxF9FO3OLXmBgPwIhAJEOctRAJmVFE/K29bsrf71xiezZWjpP0mC/lfycTRP5"}],"unpackedSize":52874},"main":"dist/index.js","name":"@aduptive/instagram-scraper","types":"dist/index.d.ts","author":{"name":"aduptive"},"engines":{"node":">=18.0.0"},"gitHead":"759f8e01d0ca0c70ac8cec2ffb6c20a17e0a2529","license":"MIT","scripts":{"lint":"eslint . --ext .ts","test":"jest","build":"tsc","clean":"rimraf dist","format":"prettier --write \"src/**/*.ts\"","prepare":"npm run clean && npm run build","version":"npm run format && git add -A src","preversion":"npm run lint","postversion":"git push && git push --tags","prepublishOnly":"npm test && npm run lint"},"version":"2.1.1","_npmUser":{"name":"aduptive","email":"aduptive@gmail.com"},"homepage":"https://github.com/aduptive/instagram-scraper#readme","keywords":["instagram","scraper","typescript","social-media","data-collection","web-scraping","public-content","no-authentication","rate-limiting","mobile-first","media","video","carousel"],"repository":{"url":"git+https://github.com/aduptive/instagram-scraper.git","type":"git"},"_npmVersion":"10.9.4","description":"Zero-dependency TypeScript library for scraping public Instagram profiles, posts and media without login, with built-in retries and rate limiting","directories":{},"maintainers":[{"name":"aduptive","email":"aduptive@gmail.com"}],"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.56.0","rimraf":"^5.0.5","ts-jest":"^29.2.5","prettier":"^3.2.1","typescript":"^5.3.3","@types/jest":"^29.5.14","@types/node":"^20.11.0","@typescript-eslint/parser":"^6.18.1","@typescript-eslint/eslint-plugin":"^6.18.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/instagram-scraper_2.1.1_1789522730236_0.7437412556974621"}}},"time":{"created":"2024-10-29T01:22:29.337Z","modified":"2026-09-16T01:38:50.487Z","1.0.0":"2024-10-29T01:22:29.677Z","1.0.3":"2024-12-14T04:28:54.853Z","2.0.0":"2026-08-24T20:42:57.904Z","2.1.1":"2026-09-16T01:38:50.340Z"},"bugs":{"url":"https://github.com/aduptive/instagram-scraper/issues"},"author":{"name":"aduptive"},"license":"MIT","homepage":"https://github.com/aduptive/instagram-scraper#readme","keywords":["instagram","scraper","typescript","social-media","data-collection","web-scraping","public-content","no-authentication","rate-limiting","mobile-first","media","video","carousel"],"repository":{"url":"git+https://github.com/aduptive/instagram-scraper.git","type":"git"},"description":"Zero-dependency TypeScript library for scraping public Instagram profiles, posts and media without login, with built-in retries and rate limiting","maintainers":[{"name":"aduptive","email":"aduptive@gmail.com"}],"readme":"# instagram-scraper\n\nFetch public Instagram profiles, posts and media, no login required. TypeScript, zero dependencies (native fetch), built-in retries and rate limiting so you don't get your IP blocked in the first minute.\n\nRequires Node 18 or newer.\n\n## Install\n\n```bash\nnpm install @aduptive/instagram-scraper\n```\n\n## Usage\n\n### Recent posts of a profile\n\n```typescript\nimport { InstagramScraper } from '@aduptive/instagram-scraper';\n\nconst scraper = new InstagramScraper();\nconst results = await scraper.getPosts('instagram', 12);\n\nif (results.success && results.posts) {\n  console.log(`got ${results.posts.length} posts`);\n  await scraper.saveToJson(results, 'posts.json');\n} else {\n  console.error(results.error);\n}\n```\n\nEach post comes with id, shortcode, caption, like/comment counts, timestamp, post URL and the media items (images, videos and carousel children, with dimensions).\n\n### Profile info\n\n```typescript\nconst result = await scraper.getProfile('instagram');\n\nif (result.success && result.profile) {\n  const p = result.profile;\n  console.log(p.full_name, p.followers, p.following, p.posts_count);\n  console.log(p.biography, p.is_verified, p.profile_pic_url);\n}\n```\n\n### A single post or reel by URL\n\n```typescript\nconst result = await scraper.getPost('https://www.instagram.com/reel/CxSEjxfyJtN');\n// also accepts /p/ and /tv/ URLs, or a bare shortcode\n\nif (result.success && result.post) {\n  console.log(result.post.media_type, result.post.media_items);\n}\n```\n\n### Progress and cancellation\n\n`getPosts` waits 1-2s between posts, so longer runs take a while. You can watch progress and cancel mid-run:\n\n```typescript\nconst controller = new AbortController();\n\nconst results = await scraper.getPosts('instagram', 12, {\n  signal: controller.signal,\n  onProgress: ({ fetched, total, currentPost }) => {\n    console.log(`${fetched}/${total} ${currentPost.shortcode}`);\n  },\n});\n```\n\nOn failure or cancellation, `results.posts` still contains whatever was collected before it stopped (with `success: false` and `code: 'ABORTED'` when cancelled).\n\n## Config\n\nAll optional:\n\n```typescript\nconst scraper = new InstagramScraper({\n  maxRetries: 3,           // retries for network/timeout/5xx errors\n  minDelay: 1000,          // random pause between requests, in ms\n  maxDelay: 3000,\n  timeout: 10000,          // per-request timeout in ms\n  rateLimitPerMinute: 30,  // hard cap on requests per minute\n});\n```\n\nNotes on how these behave:\n\n- `maxRetries` is the number of total attempts, and only transient errors (network, timeout, Instagram 5xx) are retried. A 429, 404 or 403 fails immediately, since retrying those just digs the hole deeper. A 200 with a non-JSON body (login wall, HTML error page) fails immediately too, as `PARSE_ERROR`.\n- Getting rate limited (429) or blocked (403) mid-collection stops the run: `getPosts` returns `success: false` with the reason and whatever posts were collected so far. It never keeps hammering a blocked IP.\n- The rate limit is a sliding one-minute window over every request the scraper makes (profile + media), on top of the random min/max delay.\n\n## Error handling\n\nMethods don't throw for scraping failures. Check `success`:\n\n```typescript\nconst results = await scraper.getPosts('someuser');\nif (!results.success) {\n  // results.error is a message, results.code is one of:\n  // RATE_LIMITED, PROFILE_NOT_FOUND, POST_NOT_FOUND, ACCESS_DENIED,\n  // TIMEOUT, ABORTED, NETWORK_ERROR, SERVER_ERROR\n  console.error(results.code, results.error);\n}\n```\n\n## Limitations\n\n- Public profiles only.\n- Without login Instagram only serves the ~12 most recent posts of a profile, so a higher `limit` won't get you more than that.\n- This relies on Instagram's web API, which they can change whenever they feel like it. If something breaks, [open an issue](https://github.com/aduptive/instagram-scraper/issues).\n\n## Scraping multiple profiles\n\nDo it sequentially, and give it some breathing room between accounts. A shared scraper instance keeps the rate-limit window across profiles:\n\n```typescript\nconst scraper = new InstagramScraper();\n\nfor (const username of ['nasa', 'natgeo']) {\n  const results = await scraper.getPosts(username, 12);\n  console.log(username, results.success ? results.posts?.length : results.error);\n  await new Promise((r) => setTimeout(r, 60_000));\n}\n```\n\nRunning scrapes in parallel against Instagram is the fastest way to get rate limited. Don't.\n\n## Changed in 2.1\n\n- Media details are now fetched with the post's numeric media id (the correct\n  identifier for that endpoint), with a one-time shortcode fallback.\n- A 429/403 during media enrichment now stops the run and reports it, instead\n  of silently returning media-less posts as success.\n- Carousels are classified from the post's structural fields, and when the\n  media-info call fails the media already present in the profile response is\n  used as a fallback.\n- An already-aborted `AbortSignal` is respected before any request is made.\n- Invalid JSON responses report `PARSE_ERROR` (no retry) instead of\n  `NETWORK_ERROR` with retries.\n- The constructor validates its config (`INVALID_CONFIG` error on nonsense\n  like `rateLimitPerMinute: 0`), and `getPosts` rejects a non-positive limit.\n\n## Migrating from 1.x\n\n- Node 18+ is now required (axios was replaced by native fetch).\n- `getPosts` on failure now also returns the partially collected `posts` (before, `posts` was absent on failure).\n- Everything else is backwards compatible; `getProfile`, `getPost`, `signal` and `onProgress` are new.\n\n## License\n\nMIT. Use it responsibly and check Instagram's terms of service. This is for collecting public data, not for abuse.\n","readmeFilename":"README.md"}