npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brahimtimezghine/screenshot-api

v1.0.0

Published

Ultra-fast, memory-efficient Node.js screenshot service — 200MB RAM vs 8GB for vanilla Puppeteer

Readme

screenshot-api

Ultra-fast, memory-efficient Node.js screenshot service
200 MB RAM usage · 1–2 s per capture · 500+ concurrent requests · 99.5 %+ reliability


Why screenshot-api?

| Metric | Puppeteer (vanilla) | screenshot-api | |--------|--------------------|--------------------| | RAM at scale | 8–10 GB | 200–300 MB | | Latency (p50) | 3–5 s | 1–2 s | | Concurrent reqs | 10–20 | 500+ | | Error rate | 5–10 % | < 0.5 % | | Monthly cost | $500+ | $14 (Pro) |

Key techniques:

  • Browser pool with health-checks & auto-recycling (no memory leaks)
  • Two-tier cache (in-process LRU + Redis) — repeat URLs return instantly
  • Resource interception blocks fonts/media during capture
  • MemoryOptimizer polls heap every 10 s; triggers GC at 85 %
  • Smart retry via p-retry with exponential back-off

Quick Start

# Docker (recommended)
cp .env.example .env      # set JWT_SECRET
docker-compose up -d
curl http://localhost:3000/health

# Manual
npm install
cp .env.example .env
npm run dev

API at a Glance

1. Register & Login

# Register
curl -X POST http://localhost:3000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"SecurePass1","name":"You"}'

# Login
TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"SecurePass1"}' | jq -r .token)

2. Capture a screenshot

# → raw PNG bytes
curl -X POST http://localhost:3000/api/v1/screenshot \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","format":"png"}' \
  --output screenshot.png

# → JSON with base64 data
curl -X POST "http://localhost:3000/api/v1/screenshot?json=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","full":true,"mobile":true}'

3. Batch capture

curl -X POST http://localhost:3000/api/v1/screenshot/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls":["https://example.com","https://github.com"],"concurrency":2}'

4. Check usage

curl http://localhost:3000/api/v1/usage \
  -H "Authorization: Bearer $TOKEN"

Capture Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | url | string | required | Target URL (http/https) | | full | boolean | false | Full-page capture | | mobile | boolean | false | Mobile viewport (iPhone 14) | | device | string | null | desktop laptop tablet mobile mobile-landscape | | width | number | 1920 | Viewport width (320–3840) | | height | number | 1080 | Viewport height (240–2160) | | format | string | png | png jpeg webp | | quality | number | 90 | JPEG/WebP quality (1–100) | | delay | number | 0 | Extra wait after load (seconds) | | waitUntil | string | networkidle2 | Puppeteer wait strategy | | waitForSelector | string | null | CSS selector to wait for | | timeout | number | 30000 | Navigation timeout (ms) | | compress | boolean | false | Resize to fit 1920×1080 | | useCache | boolean | true | Enable L1/L2 cache | | clip | object | null | {x,y,width,height} crop |


Pricing

| Tier | Quota | Price | |------|-------|-------| | Free | 1,000 screenshots/month | $0 | | Pro | 100,000 screenshots/month | $14/month | | Enterprise | Unlimited | Custom |


Project Structure

screenshot-api/
├── src/
│   ├── core/
│   │   ├── BrowserManager.js     # Pool of Puppeteer instances
│   │   ├── PoolManager.js        # Generic job queue (p-queue + p-retry)
│   │   ├── ScreenshotEngine.js   # Main capture façade
│   │   └── MemoryOptimizer.js    # Heap monitoring + GC
│   ├── rendering/
│   │   ├── Renderer.js           # Page navigation + screenshot
│   │   ├── CacheLayer.js         # L1 LRU + L2 Redis
│   │   └── ViewportHandler.js    # Viewport/UA resolution
│   ├── api/
│   │   ├── ApiServer.js          # Express app factory
│   │   ├── RouterV1.js           # All v1 routes
│   │   ├── controllers/          # screenshotController, authController
│   │   └── middleware/           # auth, rateLimiter, validate, errorHandler
│   ├── database/
│   │   ├── db.js                 # Mongoose connection helper
│   │   └── models/               # User, Subscription, UsageLog
│   ├── utils/
│   │   ├── logger.js             # Winston (console + rotating files)
│   │   ├── urlValidator.js       # URL normalise + security checks
│   │   ├── imageProcessor.js     # Sharp post-processing helpers
│   │   └── config.js             # Centralised env-based config
│   └── index.js                  # Bootstrap entry-point
├── tests/
│   ├── unit/                     # core.test.js, utils.test.js
│   └── integration/              # api.test.js (supertest + mocked engine)
├── scripts/
│   ├── migrate.js                # DB migrations + optional admin seed
│   └── benchmark.js              # Load-test against live instance
├── config/
│   ├── development.json
│   ├── production.json
│   └── test.json
├── docs/
│   ├── API.md                    # Full API reference
│   ├── ARCHITECTURE.md           # System design
│   ├── INSTALLATION.md           # Setup guide
│   └── EXAMPLES.md               # Code examples
├── Dockerfile                    # Multi-stage production image
├── docker-compose.yml            # App + MongoDB + Redis
├── .env.example                  # All environment variables
└── package.json

Configuration

Copy .env.example to .env.
Essential variables:

PORT=3000
MONGO_URL=mongodb://localhost:27017/screenshot_api
REDIS_URL=redis://localhost:6379
JWT_SECRET=<64-char-random-string>
BROWSER_WORKERS=4
NODE_ENV=production

Full list: see .env.example


Scripts

npm start               # Production start
npm run dev             # Development with nodemon
npm test                # Jest tests with coverage
npm run migrate         # Create DB collections & indexes
npm run benchmark       # Load test (set BENCH_TOKEN first)
npm run docker:build    # Build Docker image
npm run docker:run      # Start full stack (docker-compose)

Health & Monitoring

# Health endpoint (no auth)
curl http://localhost:3000/health

# System status (auth required)
curl http://localhost:3000/api/v1/status \
  -H "Authorization: Bearer $TOKEN"

Response includes browser-pool, cache hit rate, and memory telemetry.


Use Cases

| Use Case | Typical Volume | |----------|---------------| | SEO monitoring | 5,000–50,000/day | | Competitive intelligence | 1,000–10,000/day | | Visual regression testing | 100–1,000/run | | Report generation | 500–5,000/day | | Design verification | 100–500/day |


License

MIT © screenshot-api contributors


Performance Architecture

Request → Express → Auth → Validate → ScreenshotEngine
                                            │
                              ┌─────────────┴──────────────┐
                              │                            │
                         CacheLayer (hit)          BrowserManager (miss)
                              │                            │
                         return <1ms              Puppeteer page
                                                   Renderer.render()
                                                   Sharp post-process
                                                   CacheLayer.set()
                                                   return ~1.5s

Two replicas behind a load-balancer share Redis → effective cache hit rate approaches 60–80 % for common URLs.