How to Use a Screenshot API with a Node.js App Hosted on AWS in India
Call a screenshot API safely from Node.js, handle its response, and deploy the app to AWS Mumbai or Hyderabad without assuming where the provider processes captures.
Call the screenshot API from your Node.js backend, keep its key in server-side secret storage, and handle the response according to that provider’s documented contract. Deploy the caller in an AWS region that fits your service availability, latency, and operational needs: AWS lists Mumbai (ap-south-1) and Hyderabad (ap-south-2), with Hyderabad requiring opt-in. Hosting your app in India does not establish where an external screenshot provider renders pages or stores images.
This guide uses Screenshot API’s documented POST example for the do-it-yourself integration. That example returns JSON, and its documentation describes a CDN URL or redirect-to-download behavior. Confirm the current response shape before production use. Do not combine this endpoint with another provider’s host, request format, authentication, or response handling.
1. Choose and verify the API contract
Before writing the handler, check the selected provider’s current reference for these details:
- Request: HTTP method, endpoint, authentication header, and JSON or query parameter names.
- Response: raw image bytes, JSON with a URL, JSON with base64 data, or a redirect. These formats require different handling.
- Capture controls: viewport, full-page behavior, format, selector, wait conditions, cookies, and headers.
- Failures: HTTP status codes, target-page status, timeout behavior, quota reporting, and rate limits.
- Data handling: render location, logs, image retention, storage location, deletion, and any contractual region controls.
For example, Screenshot API documents a POST request that returns JSON. Screenshot API.net documents a GET endpoint whose body contains image bytes, as well as a separate capture option returning JSON with base64 image data and page status. Those are different contracts; code for one cannot be assumed to work with the other.
2. Put the capture behind your Node.js backend
Your browser or client should call your own application. Your backend then calls the screenshot service. This keeps the provider key out of browser JavaScript, client responses, source control, and URLs that may appear in logs.
AWS recommends Secrets Manager for sensitive credentials such as API keys and authorization tokens. Retrieve the secret on the server using your deployment’s approved secret mechanism, restrict access to the function or service that needs it, and rotate it according to your policy. Avoid placing a production key in a query string.
3. Make a Node.js request
The following follows Screenshot API’s published Node.js example: POST JSON with a bearer token, then parse JSON. It is a provider-documented example, not a claim of independent testing. The example does not show how the returned JSON is shaped, so inspect the provider’s current reference and adapt the response handling before using it in production.
const response = await fetch('https://api.screenshot-api.org/api/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SCREENSHOT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
viewport: { width: 1280, height: 720 },
format: 'png',
fullPage: true,
}),
});
if (!response.ok) {
const detail = await response.text();
throw new Error(`Screenshot API returned ${response.status}: ${detail}`);
}
const data = await response.json();
// Use the documented response field for the image URL or download result.
console.log(data);
Modern Node.js runtimes include fetch. If using a runtime without it, use a maintained HTTP client or the built-in HTTP modules. No provider SDK is inherently required for an HTTP API call.
A minimal Express route pattern
This example illustrates the application boundary and error handling. It expects the provider to return JSON, as in the documented example. Add authentication and authorization appropriate to your own application, and validate which URLs your users may request before enabling the route.
import express from 'express';
const app = express();
app.use(express.json());
app.post('/api/screenshots', async (req, res) => {
const { url } = req.body ?? {};
let target;
try {
target = new URL(url);
} catch {
return res.status(400).json({ error: 'Provide a valid URL.' });
}
if (!['http:', 'https:'].includes(target.protocol)) {
return res.status(400).json({ error: 'Only HTTP and HTTPS URLs are supported.' });
}
try {
const upstream = await fetch('https://api.screenshot-api.org/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SCREENSHOT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: target.href,
viewport: { width: 1280, height: 720 },
format: 'png',
fullPage: true,
}),
signal: AbortSignal.timeout(90_000),
});
if (!upstream.ok) {
const detail = await upstream.text();
console.error('Screenshot provider error', upstream.status, detail);
return res.status(502).json({ error: 'Screenshot provider request failed.' });
}
const result = await upstream.json();
// Replace this with the documented field and your chosen delivery design.
return res.json(result);
} catch (error) {
console.error('Screenshot request failed', error);
return res.status(502).json({ error: 'Could not complete screenshot request.' });
}
});
app.listen(process.env.PORT ?? 3000);
URL validation here checks syntax and scheme only. If users can submit arbitrary URLs, also defend against server-side request forgery: apply an explicit host policy where possible, block private and link-local destinations, account for DNS resolution and redirects, and avoid exposing internal network services. Do not rely on a simple string-prefix check.
4. Handle the response in the format the provider returns
The response contract determines what your app should send to its caller:
| Provider response | Typical handling | Check before shipping |
|---|---|---|
| Raw image bytes | Read as binary and stream or save with the provider’s content type. | Confirm status, MIME type, and whether errors use a non-image body. |
| JSON with hosted image URL | Parse JSON and return or fetch the documented URL. | Check URL expiration, access rules, and retention terms. |
| JSON with base64 image | Decode the documented field with Buffer.from(value, 'base64'). |
Validate the MIME type and avoid logging the full payload. |
| Redirect or download response | Follow or relay the redirect according to the API contract. | Check redirect status, destination trust, and binary headers. |
Do not call response.json() when the endpoint returns image bytes, and do not treat a hosted image URL as if it were the image itself. The POST example above parses JSON because that is how the provider publishes it; adapt only after confirming the live contract.
5. Pick capture settings deliberately
Screenshot API’s published example includes a 1280 by 720 viewport, PNG format, and full-page capture. The Screenshot API.net documentation lists a different set of controls, including width, height, full-page capture, image format, quality, delay, and timeout. Use only parameters supported by the provider and endpoint you selected.
- Viewport: choose dimensions matching the layout you need to inspect. A responsive page can render differently at different widths.
- Full page: useful for long pages, but can increase render time and image size. Pages with lazy-loaded content may need provider-specific scrolling or wait behavior.
- Format: PNG is lossless and useful for fine interface details; JPEG can suit photographic content; WebP may reduce size if the provider and consumers support it. Confirm actual supported formats.
- Wait and timeout: distinguish a deliberate delay from waiting for a page condition. Longer waits consume more request time and can still fail if the page never settles.
- Access requirements: pages behind login, bot checks, or access controls may produce an error page rather than the intended content. Use supported cookies or headers only where authorized.
6. Deploy the Node.js app to AWS in India
- Choose the compute service and runtime. Lambda supports Node.js code deployed as a ZIP archive or container image. Other AWS hosting choices also work if they can make outbound HTTPS requests to the provider.
- Package dependencies. For Lambda ZIP deployments, include external Node.js dependencies with the handler or use a Lambda layer. A simple call using built-in
fetchdoes not require an AWS SDK client for the screenshot provider. - Configure the secret. Give the function or service permission to retrieve the API key from Secrets Manager, and keep secret access limited to the required workload.
- Set execution limits. Configure the function timeout and any upstream proxy or gateway limits to accommodate screenshot rendering. Keep the application request deadline within those limits and return a clear timeout error.
- Select the AWS region. AWS lists Mumbai (
ap-south-1) and Hyderabad (ap-south-2). Mumbai is enabled by default; Hyderabad is opt-in. Check service and feature availability and account opt-in before choosing. - Deploy and inspect logs. Verify runtime compatibility, outbound connectivity, secret permissions, and the provider’s actual response. Do not log API keys, authorization headers, or sensitive page content.
AWS’s Lambda deployment guide describes ZIP and container deployments and assigning dependencies through the package or a layer. AWS also makes dependency updates and security patching the function owner’s responsibility. Confirm the currently supported Node.js runtime at deployment time; runtime choices change.
7. Understand region, privacy, and operational boundaries
The AWS region controls where your calling application runs. It does not prove where the external screenshot service processes the submitted page, stores the resulting image, or retains logs. The provider documentation reviewed for this guide does not settle those points.
If pages or URLs contain sensitive information, ask the provider about rendering geography, data retention, request logging, image storage and deletion, region controls, and contractual commitments before sending them. Treat both the target URL and screenshot as potentially sensitive data.
For reliability, check both the screenshot API’s HTTP response and the target page’s final status when the provider exposes it. A successful API response can still contain a screenshot of a login screen, bot check, or access-denied page. Set bounded timeouts, report failures clearly, and retry only transient failures with a limit and backoff; repeated retries can waste time and provider quota.
8. cURL and Python request examples
These examples show Screenshot API’s documented POST JSON contract. They parse JSON rather than assuming the response is an image file. Inspect the actual response fields in the provider’s current documentation before wiring image delivery into your application.
cURL
curl --fail-with-body \
-X POST 'https://api.screenshot-api.org/api/v1/screenshot' \
-H "Authorization: Bearer $SCREENSHOT_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"url":"https://example.com","viewport":{"width":1280,"height":720},"format":"png","fullPage":true}'
Python
import os
import requests
response = requests.post(
'https://api.screenshot-api.org/api/v1/screenshot',
headers={
'Authorization': f"Bearer {os.environ['SCREENSHOT_API_KEY']}",
'Content-Type': 'application/json',
},
json={
'url': 'https://example.com',
'viewport': {'width': 1280, 'height': 720},
'format': 'png',
'fullPage': True,
},
timeout=90,
)
response.raise_for_status()
data = response.json()
print(data)
# Use the documented image URL or download field from data.
9. Troubleshoot common failures
| Symptom | Likely cause | What to do |
|---|---|---|
| 401 or 403 from the screenshot API | Missing, invalid, or unauthorized API key; provider may also report access restrictions. | Check the secret value and authorization scheme without printing the key. Read the provider error body safely and verify account permissions. |
| The app returns JSON where an image was expected | The endpoint returns a hosted URL or JSON metadata rather than raw bytes. | Follow the documented response schema; fetch or relay the image only using the documented field and access rules. |
| Image parsing fails or output is corrupt | Code parsed binary as JSON/text, or decoded the wrong JSON field. | Check status and Content-Type; handle raw bytes, base64, and hosted URLs as distinct cases. |
| Screenshot shows a login or error page | The target returned an authentication, bot-check, or access-denied page. | Inspect the final page status if exposed. Use provider-supported authentication settings only when permitted, or capture a page accessible to the service. |
| Timeouts on long pages | Slow page resources, oversized full-page capture, or wait settings exceed runtime limits. | Set a bounded provider timeout, increase the AWS execution timeout within platform limits, reduce capture scope, and choose a suitable wait condition. |
| Secret is unavailable in Lambda | Missing IAM permission, incorrect secret identifier, network/configuration issue, or deployment configuration error. | Check the function role and secret configuration; log error identifiers, not secret values. |
| Works locally but not in AWS | Runtime mismatch, missing packaged dependency, outbound network restriction, or incorrect region setup. | Test the deployed package and runtime together, inspect network egress, and confirm the selected region supports the required service. |
| Unexpected quota or rate-limit failure | Provider account limits or request volume exceeded the current allowance. | Inspect provider status and quota headers or response fields, queue work where appropriate, and apply bounded retries only when the failure is transient. |
10. Performance, reliability, and cost
Rendering a page is remote work whose duration depends on the target and provider. Full-page captures, slow resources, and additional waiting can increase latency. Keep the HTTP request path bounded; for longer workflows, use a queue or asynchronous job design if your provider supports it. Cache a capture only when your freshness requirements allow it, and account for authenticated or personalized pages before sharing cached results.
Estimate usage from your own expected capture volume and the provider’s current price and quota documentation. The sources reviewed here do not establish comparable prices, latency, or reliability figures, so do not assume them. Track successful captures, provider errors, target page status, timeouts, and quota usage separately. Avoid recording sensitive URLs or page contents in general application logs.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Make one GET request from your Node.js server; its API returns an image or PDF. Keep the access key server-side, and see the ScreenshotNeo API documentation for the available parameters.
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(`ScreenshotNeo returned ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
// Save image or send it with the response content type documented for your request.
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card.
FAQ
Do I need a screenshot SDK for Node.js?
No. A documented HTTP endpoint can be called with Node.js fetch or another HTTP client. Use an SDK only if the provider offers one and it suits your needs.
Does deploying the caller in AWS India keep screenshots in India?
No such conclusion follows from the caller’s region. Confirm the screenshot provider’s processing and storage geography directly.
Should my frontend call the screenshot provider directly?
For a production integration, call it from your backend so the provider key stays server-side and your application can validate requests and control access.
Which India AWS region should I choose?
Compare service availability, proximity to your users, account opt-in, and your legal and operational needs. AWS lists Mumbai and Hyderabad; Hyderabad requires opt-in.


