Use ScreenshotOne from server-side code in your Next.js app: keep the access key in a server-only environment variable, call ScreenshotOne’s HTTPS /take endpoint (or its JavaScript/TypeScript SDK), and return the resulting image or other requested format from a Route Handler. Validate target URLs, preserve the upstream content type, and handle JSON errors instead of treating every response as an image.
Choose a server-side integration
A Next.js server endpoint is the right place to call ScreenshotOne because it can keep the access key private, restrict what callers may capture, and return the binary result to your application. For a small integration, call the API with fetch; if you prefer a typed vendor client, use ScreenshotOne’s screenshotone-api-sdk. ScreenshotOne documents both approaches in its Getting Started and JavaScript and TypeScript SDK guides.
The example below uses the App Router Route Handler convention documented for Next.js 13: place it in app/api/screenshot/route.ts. Route Handler details can vary by Next.js version, so check the documentation for the version installed in your project: Next.js 13 Route Handlers.
Set up the access key safely
- Create or copy an access key from ScreenshotOne’s account page, following its API keys guide.
- Set it in your deployment platform’s server-side environment configuration as
SCREENSHOTONE_ACCESS_KEY. For local development, use an environment file excluded from source control. - Do not prefix the variable with
NEXT_PUBLIC_, put it in a client component, commit it, or expose an ordinary generated ScreenshotOne URL containing it on a public page.
ScreenshotOne’s documentation says, “Treat your API key like a password.” Its access key authenticates API requests; the separate secret key is used for signing or webhook verification. If a key is compromised, replace it. For a URL that must be shared, use the SDK’s signed-URL method rather than exposing an unsigned URL.
Recommended Free Tools
#1 Best Overall
Build a Route Handler that returns the screenshot
This TypeScript example accepts a URL in a POST body, allows only HTTP or HTTPS targets, requests a PNG, and returns the upstream bytes with their content type. It rejects non-success API responses as JSON errors. Save it as app/api/screenshot/route.ts in an App Router project.
const allowedProtocols = new Set(["http:", "https:"]);
export async function POST(request: Request) {
let body: unknown;
try {
body = await request.json();
} catch {
return Response.json({ error: "Request body must be valid JSON" }, { status: 400 });
}
const target = (body as { url?: unknown })?.url;
if (typeof target !== "string") {
return Response.json({ error: "Provide a URL string" }, { status: 400 });
}
let targetUrl: URL;
try {
targetUrl = new URL(target);
} catch {
return Response.json({ error: "Provide a valid absolute URL" }, { status: 400 });
}
if (!allowedProtocols.has(targetUrl.protocol)) {
return Response.json({ error: "Only HTTP and HTTPS URLs are allowed" }, { status: 400 });
}
const accessKey = process.env.SCREENSHOTONE_ACCESS_KEY;
if (!accessKey) {
return Response.json({ error: "Screenshot service is not configured" }, { status: 500 });
}
const params = new URLSearchParams({
url: targetUrl.toString(),
access_key: accessKey,
format: "png",
});
let upstream: Response;
try {
upstream = await fetch(`https://api.screenshotone.com/take?${params}`);
} catch {
return Response.json({ error: "Could not reach the screenshot service" }, { status: 502 });
}
if (!upstream.ok) {
const error = await upstream.json().catch(() => null);
return Response.json(
{ error: error?.error?.message ?? "Screenshot request failed" },
{ status: upstream.status },
);
}
return new Response(await upstream.arrayBuffer(), {
headers: {
"Content-Type": upstream.headers.get("content-type") ?? "image/png",
"Cache-Control": "no-store",
},
});
}
The protocol check is only a starting point, not a complete target-security policy. If callers can submit URLs, restrict them to the domains and paths your application actually needs; otherwise your endpoint may be misused to make requests to unintended destinations. Also control which screenshot options callers may set, and add application-level authorization or rate limits where appropriate.
Call the route from a client
The browser calls your own endpoint, not ScreenshotOne directly, so the access key stays on the server. For example, a client component can submit a target and display the returned image:
const response = await fetch("/api/screenshot", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com" }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error ?? "Screenshot failed");
}
const imageUrl = URL.createObjectURL(await response.blob());
// Set imageUrl as the src of an <img>; revoke it when no longer needed.
Choose the right request and output format
ScreenshotOne supports GET and POST requests. GET is convenient for short option sets; POST JSON is preferable for large HTML or Markdown input rather than putting that content in a query string. The documented maximum POST body size is 100 MiB; treat that as a service limit, not a recommended payload size. See Getting Started.
Rank #3
The API can return image formats including PNG, JPEG, WebP, and AVIF, as well as PDF, HTML, or Markdown depending on the requested options. For images, pass the returned bytes through and preserve the returned Content-Type. For a different output, set the relevant ScreenshotOne option and adjust your endpoint and consumer to expect that response type; the available settings are documented in Screenshot Options.
Use the official JavaScript or TypeScript SDK
Install the documented package from your project root:
Rank #4
npm install screenshotone-api-sdk
ScreenshotOne’s SDK guide documents creating a Client from the access and secret keys, setting the target with TakeOptions.url(...), calling client.take(options), and reading the response as an ArrayBuffer. Recent SDK methods are asynchronous. Keep this code in server-only modules, and follow the current package documentation for exact imports and method signatures: JavaScript and TypeScript SDK examples. Use generateSignedTakeURL() when you need a shareable URL; a normal SDK-generated URL is not signed and can expose the access key.
Security and edge cases
- Protect credentials: Never send the access key to a browser or expose it in an unsigned shared URL. Use HTTPS; ScreenshotOne warns that HTTP does not encrypt keys, authorization headers, or cookies in transit.
- Validate and constrain inputs: Validate URL, HTML, or Markdown inputs, allow only needed ScreenshotOne options, and apply your own access controls and usage limits. Basic URL parsing does not prevent every unwanted destination.
- Handle authenticated pages carefully: ScreenshotOne documents sending authorization headers or cookies for pages you own or are authorized to access. Obtaining session cookies may require custom sign-in code. Do not forward user credentials or session cookies without an explicit, secure design; see Screenshot authenticated pages.
- Do not assume every successful HTTP response is a PNG: Return the upstream content type and use the output format your route expects. API errors are JSON with an error code and message, so parse them separately from binary output.
Troubleshoot common problems
| Symptom | Likely cause | What to check |
|---|---|---|
| ScreenshotOne returns an authentication error | The access key is missing, incorrect, or not reaching the server process. | Check the server environment variable name and deployment configuration. Do not solve this by placing the key in client-side code. |
| The app returns an HTML or JSON error where an image was expected | The upstream request failed or the endpoint returned a non-image format. | Check upstream.ok, parse the JSON error body, and confirm the requested ScreenshotOne format and returned content type. |
| The route works locally but not after deployment | The production environment may not have the server-side key, or the route may differ under the installed Next.js version/runtime. | Set the secret in the hosting platform and verify the Route Handler conventions for your Next.js version. |
| A target URL is rejected | It may be malformed, use a disallowed protocol, or fail your application’s allowlist. | Submit an absolute HTTP or HTTPS URL and review the route’s validation policy. |
| Large HTML or Markdown input fails | Putting large content in a GET query string can exceed practical URL limits. | Send it in a POST JSON body and remain within ScreenshotOne’s documented 100 MiB maximum request body. |
Or skip the browser setup
For a one-request screenshot API alternative, ScreenshotNeo returns PNG, JPEG, WebP, or PDF from a GET request. It removes cookie/consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed; and its MCP server lets AI agents use screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesExample request using the API key and target URL in the query:
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Sign up for 1,000 free screenshots a month with no card.
Frequently Asked Questions
Can I call ScreenshotOne directly from a Next.js client component?
You can make a browser request technically, but you should not put the ScreenshotOne access key in client-side code. Proxy the request through server-side code so the key stays private.
Does ScreenshotOne support a Next.js example?
ScreenshotOne publishes a public Next.js screenshots example repository, but the safest implementation details depend on the repository contents and your installed Next.js version: https://github.com/screenshotone/next.js-screenshots-example.
Quick 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.

