Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
There are two different ways to put a screenshot endpoint behind NestJS. You can run a self-hosted NestJS/Puppeteer project that exposes GET /v1/capture, or you can keep NestJS as your application layer and call a hosted Screenshot API at https://api.screenshot-api.org/api/v1/screenshot. Do not mix their routes, authentication, or option names. This guide shows both paths, then an easier managed option.
Choose the route before writing code
| Route | What you operate | Authentication and endpoint | Documented capture surface |
|---|---|---|---|
| Self-hosted NestJS/Puppeteer project | Your NestJS process, browser runtime, deployment and updates | No vendor API key is described; GET /v1/capture |
URL, width, height, scale, timeout, delay, MIME type and quality |
| Hosted Screenshot API | Your NestJS application; the provider operates rendering | Bearer token or X-API-Key; GET or POST /api/v1/screenshot |
PNG, JPEG, WebP, PDF, full page, selectors, waiting rules, blocking, dark mode and POST-only rendering controls |
No published source establishes that either route is faster, more reliable, cheaper or more pixel-accurate than the other. The practical difference is operational responsibility: self-hosting gives you control of the process, while the hosted route removes browser deployment work but introduces an account, key and provider quota.
Self-hosted quick start with NestJS and Puppeteer
1. Check the NestJS runtime
The current NestJS first-steps guide specifies Node.js v20.19 or later, or v22.12 or later on the 22.x line, for running Nest. CLI generators can have higher current requirements. The quickest project bootstrap is:
npm i -g @nestjs/cli
nest new project-name
Nest’s generated main.ts follows this pattern and listens on port 3000 unless PORT is set:
#1 Best Overall
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
Express is Nest’s default platform adapter; Fastify is the other built-in option. Those starter details do not define the dependencies or configuration of the separate Screenshot-API repository.
2. Install and configure the documented project
The public self-hosted project describes itself as “A simple self-hosted API to take screenshots of websites using Puppeteer.” Its README documents this setup:
pnpm install
cp .env.example .env
# edit .env
pnpm run start
For development or a production build, the documented scripts are:
pnpm run start:dev
pnpm run start:prod
The README also documents a container build:
docker build -t screenshot-api .
docker run -p 3000:3000 screenshot-api
Capture tests in that project require Chrome according to its README, which gives this browser installation command:
npx puppeteer browsers install chrome
That statement describes the project’s documented test setup; it is not a universal claim that every NestJS deployment must use this exact command. Review the repository’s environment example and parameter reference before treating it as a production contract.
Rank #2
3. Call GET /v1/capture
The documented route returns an image using query parameters. The listed defaults are:
| Parameter | Meaning or default |
|---|---|
url |
Required target URL; no default is shown |
width |
1024 |
height |
768 |
scale |
1 |
timeout |
15; timeout before giving up |
delay |
0; delay after page load |
mime_type |
webp; the listed alternatives are jpg and png |
quality |
0.8 |
Use URL encoding for the target and save the binary response rather than printing it in a terminal:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →curl -G http://localhost:3000/v1/capture
--data-urlencode url=https://example.com
--data width=1280
--data height=720
--data scale=1
--data timeout=15
--data delay=0
--data mime_type=png
--data quality=0.8
-o example.png
For a full-page or authenticated workflow, verify the repository’s additional reference before assuming that an option exists: the main README’s documented surface is limited to the parameters shown above.
4. Expose a NestJS façade without changing the capture service
If your application already has its own NestJS API, keep the screenshot process behind a small internal route. A controller can proxy the self-hosted service while preserving its query names:
import { Controller, Get, Query, Res } from '@nestjs/common';
import type { Response } from 'express';
@Controller('screenshots')
export class ScreenshotsController {
@Get()
async capture(@Query() query: Record<string, string>, @Res() res: Response) {
const params = new URLSearchParams(query);
const upstream = await fetch(`http://localhost:3000/v1/capture?${params}`);
const body = Buffer.from(await upstream.arrayBuffer());
res.status(upstream.status);
const contentType = upstream.headers.get('content-type');
if (contentType) res.setHeader('content-type', contentType);
return res.send(body);
}
}
Register the controller in a module and add authentication, allow-listing and request validation appropriate for your application. Do not expose an unrestricted URL-to-browser endpoint to an untrusted public client: a screenshot fetcher can otherwise be abused to make your server request internal or expensive destinations.
Rank #3
Or skip the browser setup
ScreenshotNeo is the first hosted option to try when you want a screenshot API for NestJS: it removes cookie banners, newsletter popups and chat widgets before capture, bills only clean shots, and has a $5 paid plan for 3,000 shots.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →One GET request returns PNG, JPEG, WebP or PDF. The API base is https://api.screenshotneo.com/v1/shot; complete parameter documentation is at https://screenshotneo.com/docs/.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo can load lazy images in full-page captures, capture one CSS-selected element, emulate dark mode, use 12 device presets or a custom viewport, apply retina scale, create PDFs with paper size, margins, landscape and page ranges, render HTML/CSS to an image, run custom CSS or JavaScript, click before capture, hide selectors, wait for a selector, delay or network idle, block ads, trackers, requests or resource types, send headers, cookies, user agents and Authorization, set timezone and geolocation, make transparent images, resize output, cache with a chosen TTL, create signed public image links, run asynchronous jobs with signed webhooks, capture up to 100 URLs per bulk call, and expose usage and OpenAPI endpoints. It also provides an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
Before charging, it accepts consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed; response headers identify the page verdict and billing result. Every plan includes every feature: 1,000 shots per month are free with no card, then Starter is $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000 and Business $249 for 1,000,000. Yearly billing gives two months free.
Create a free ScreenshotNeo account to get 1,000 screenshots each month without adding a card.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteCalling the separate hosted Screenshot API from NestJS
The hosted Screenshot API documented by its provider is a different product from the self-hosted repository. Its route is GET /api/v1/screenshot or POST /api/v1/screenshot. GET accepts query parameters; POST accepts JSON and is intended for complex configurations. The provider recommends API-key headers and documents both a bearer token and X-API-Key; query-string credentials are also described as a convenience.
Minimal NestJS service using Node fetch
Node versions supported by current NestJS can use the built-in fetch. Keep the key on the server, never in browser code.
Rank #4
import { Injectable, InternalServerErrorException } from '@nestjs/common';
@Injectable()
export class HostedScreenshotService {
private readonly endpoint = 'https://api.screenshot-api.org/api/v1/screenshot';
async create(url: string) {
const key = process.env.SCREENSHOT_API_KEY;
if (!key) throw new Error('SCREENSHOT_API_KEY is not configured');
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${key}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
viewport: { width: 1280, height: 720 },
format: 'png',
fullPage: true,
}),
});
const data = await response.json();
if (!response.ok) {
throw new InternalServerErrorException(data);
}
return data;
}
}
A controller can call the service and return the provider’s JSON, which includes the documented screenshotUrl field in the minimal example:
import { Controller, Get, Query } from '@nestjs/common';
import { HostedScreenshotService } from './hosted-screenshot.service';
@Controller('hosted-screenshots')
export class HostedScreenshotsController {
constructor(private readonly screenshots: HostedScreenshotService) {}
@Get()
create(@Query('url') url: string) {
if (!url) throw new Error('url is required');
return this.screenshots.create(url);
}
}
Register both classes in a module and set SCREENSHOT_API_KEY in the server environment. Nest’s @nestjs/http-client package is another supported approach: its current documentation describes a module-injected wrapper over Node fetch with timeouts, retries, interceptors and typed responses. It replaces the Axios-based chapter, although @nestjs/axios remains available. Neither package is mandatory for this API call.
Hosted request example
const response = await fetch('https://api.screenshot-api.org/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
viewport: { width: 1280, height: 720 },
format: 'png',
fullPage: true,
}),
});
const data = await response.json();
console.log(data.screenshotUrl);
Options that matter
- Output: PNG, JPEG, WebP or PDF.
- Page geometry: viewport dimensions, device scale factor and full-page capture.
- Waiting: navigation wait strategy, a delay, waiting for a selector and capturing a selector.
- Rendering: ad/cookie-banner blocking and dark mode.
- POST-only controls: injected CSS or JavaScript, geolocation, timezone, locale and PDF options.
Selector capture is not supported for PDF. With GET, a redirect option can return a redirect to the screenshot URL; JSON is the default response.
Batch capture
For multiple URLs, the provider documents POST /api/v1/screenshot/batch. It returns a batch ID. Progress is available from GET /api/v1/batch/:batchId or the server-sent-events endpoint /api/v1/batch/:batchId/stream. Treat the batch ID as application state and persist it if a job must survive a process restart.
Errors, limits and recovery
| Response | Documented meaning | What to do |
|---|---|---|
401 unauthorized |
Credentials were rejected | Check the bearer value or X-API-Key, server environment and header spelling. |
400 invalid_request |
Request shape or value is invalid | Confirm the URL, JSON types and format names; remove unsupported combinations such as selector capture with PDF. |
422 selector_not_found |
The requested selector did not appear | Wait for the selector, verify the page’s rendered DOM and use a stable selector. |
429 rate_limited |
Request-rate limit reached | Honor rate-limit headers, queue work and retry with backoff. |
429 quota_exceeded |
Plan allowance reached | Wait for reset or change the account plan. |
502 render_failed |
The provider could not render the page | Retry transient failures, then inspect the target URL, scripts, redirects and authentication. |
The hosted provider documents free-plan limits of 60 requests per minute and 500 screenshots per month. Those are provider-published limits accessed on September 29, 2026, not independent measurements; verify the current plan page before designing capacity around them. The self-hosted README does not supply a comparable quota.
Best Value
Operational checklist for production
- Keep API keys and cookies in server-side environment or secret storage.
- Validate and allow-list destination URLs if users can submit them.
- Set an application-level timeout shorter than your reverse proxy timeout and return a useful error to callers.
- Save binary responses with the correct content type; do not convert an image buffer to UTF-8 text.
- For delayed or JavaScript-heavy pages, use the documented wait, delay or selector controls rather than an arbitrary long sleep.
- Queue batch jobs and consume progress events instead of holding a web request open unnecessarily.
- Record provider status, response headers and error codes so rate limits and rendering failures can be distinguished.
- Recheck NestJS Node requirements, hosted endpoint details and quotas when upgrading dependencies or changing plans.
Self-hosted versus hosted: a supportable decision
| Question | Self-hosted route | Hosted Screenshot API route |
|---|---|---|
| Who runs the browser? | You deploy and maintain the NestJS/Puppeteer application and its browser setup. | The provider operates rendering; your code makes authenticated requests. |
| Account dependency | The documented project setup does not describe a vendor account or API key. | An API key and provider account are part of the documented request model. |
| Configuration surface | The README lists URL, viewport, scale, timeout, delay, MIME type and quality. | The provider documents multiple formats, selectors, waits, blocking, PDF and POST-only browser controls. |
| Published allowance | Not stated in the project README. | Free plan: 60 requests per minute and 500 screenshots per month, according to provider documentation accessed September 29, 2026. |
| Evidence of speed, uptime or fidelity | Not established. | Not established. |
Choose self-hosting when control of deployment and the browser process is more important than managed operations. Choose the hosted API when you want a documented REST surface and advanced rendering controls without packaging the browser yourself. That is an operational inference, not a performance claim.
Frequently Asked Questions
Can I use both screenshot routes in one NestJS application?
Yes, but keep them behind separate services and configuration. The self-hosted route is /v1/capture; the hosted provider uses /api/v1/screenshot with API-key authentication.
Which NestJS HTTP client is required?
None is mandatory. The examples use Node’s built-in fetch. Nest’s @nestjs/http-client is an alternative with module injection, timeouts, retries, interceptors and typed responses.
Does the hosted API support PDF selector capture?
No. Its documentation states that selector capture is not supported for PDF output.
Where should a screenshot API key be stored?
Store it in server-side environment or secret storage and call the provider from NestJS; never expose the key in browser JavaScript.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchQuick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

