CloudConvert API Example: Convert a URL to a Screenshot
Create a website screenshot with CloudConvert API v2 using a capture-website job, then export the result. Includes cURL, Python, Node.js, and troubleshooting.
To convert a URL to a screenshot with CloudConvert, create an API v2 job with a capture-website task and an export/url task. Set the page URL and image format, then retrieve the exported file after the job finishes. The capture task supports PNG or JPG output; the example below uses PNG. See CloudConvert’s Website Screenshot API and capture-website operation reference.
1. Get an API key
Create an API key in your CloudConvert account and keep it in a server-side secret store or an environment variable. Do not put a real key in browser code, commit it to source control, or write it to request logs. CloudConvert API v2 uses the base URL https://api.cloudconvert.com/v2 and supports API keys and OAuth 2.0; see the API introduction.
2. Create the screenshot job
The job contains two named tasks: capture-page renders the target page, and export-screenshot exposes the output URL. The operation requires url and output_format. The export task’s input refers to the capture task name.
POST https://api.cloudconvert.com/v2/jobs
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"tasks": {
"capture-page": {
"operation": "capture-website",
"url": "https://example.com",
"output_format": "png"
},
"export-screenshot": {
"operation": "export/url",
"input": "capture-page"
}
}
}
For a quick command-line request, save the response and inspect the job task statuses and exported file URL:
export CLOUDCONVERT_API_KEY="YOUR_API_KEY"
curl --fail-with-body --silent --show-error \
-X POST "https://api.cloudconvert.com/v2/jobs" \
-H "Authorization: Bearer $CLOUDCONVERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tasks": {
"capture-page": {
"operation": "capture-website",
"url": "https://example.com",
"output_format": "png"
},
"export-screenshot": {
"operation": "export/url",
"input": "capture-page"
}
}
}'
The response contains a job with its tasks and their statuses. A job is not the image itself: wait until processing completes, then read the result from the export task. CloudConvert’s quickstart describes this create, wait, and retrieve lifecycle.
3. Runnable Python example
This example creates the job, polls its status, finds the export task’s file URL, and saves the PNG. Install the dependency with python -m pip install requests. Set CLOUDCONVERT_API_KEY in the environment before running.
import os
import time
import requests
API_BASE = "https://api.cloudconvert.com/v2"
API_KEY = os.environ["CLOUDCONVERT_API_KEY"]
SOURCE_URL = "https://example.com"
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
})
job_response = session.post(
f"{API_BASE}/jobs",
json={
"tasks": {
"capture-page": {
"operation": "capture-website",
"url": SOURCE_URL,
"output_format": "png",
},
"export-screenshot": {
"operation": "export/url",
"input": "capture-page",
},
}
},
timeout=60,
)
job_response.raise_for_status()
job = job_response.json()["data"]
job_id = job["id"]
# Poll the job endpoint until every task is finished or a task fails.
deadline = time.monotonic() + 300
while True:
if time.monotonic() >= deadline:
raise TimeoutError(f"CloudConvert job {job_id} did not finish in time")
status_response = session.get(f"{API_BASE}/jobs/{job_id}", timeout=30)
status_response.raise_for_status()
job = status_response.json()["data"]
tasks = job.get("tasks", [])
failed = [task for task in tasks if task.get("status") == "error"]
if failed:
details = "; ".join(
f"{task.get('name')}: {task.get('message', 'task failed')}"
for task in failed
)
raise RuntimeError(details)
if job.get("status") == "finished":
break
if job.get("status") == "error":
raise RuntimeError(f"CloudConvert job failed: {job}")
time.sleep(2)
export_task = next(
task for task in job["tasks"] if task.get("name") == "export-screenshot"
)
files = export_task.get("result", {}).get("files", [])
if not files or not files[0].get("url"):
raise RuntimeError(f"Export task finished without a file URL: {export_task}")
# The export URL is a temporary download URL; fetch it promptly.
file_response = requests.get(files[0]["url"], timeout=90)
file_response.raise_for_status()
with open("screenshot.png", "wb") as output:
output.write(file_response.content)
print("Saved screenshot.png")
For a production worker, persist the job ID and resume polling after restarts rather than losing track of submitted work. Check the returned task data when diagnosing a failed job; do not assume a timeout means the remote job was cancelled.
4. Runnable Node.js example
This version uses Node.js’s built-in fetch and filesystem APIs. It requires a Node.js release with global fetch. Set CLOUDCONVERT_API_KEY and run the script with node screenshot.mjs.
import { writeFile } from "node:fs/promises";
const API_BASE = "https://api.cloudconvert.com/v2";
const apiKey = process.env.CLOUDCONVERT_API_KEY;
if (!apiKey) throw new Error("Set CLOUDCONVERT_API_KEY first");
async function api(path, options = {}) {
const response = await fetch(`${API_BASE}${path}`, {
...options,
headers: {
Authorization: `Bearer ${apiKey}`,
...(options.body ? { "Content-Type": "application/json" } : {}),
...options.headers,
},
});
if (!response.ok) {
throw new Error(`CloudConvert API returned HTTP ${response.status}: ${await response.text()}`);
}
return response.json();
}
const created = await api("/jobs", {
method: "POST",
body: JSON.stringify({
tasks: {
"capture-page": {
operation: "capture-website",
url: "https://example.com",
output_format: "png",
},
"export-screenshot": {
operation: "export/url",
input: "capture-page",
},
},
}),
});
const jobId = created.data.id;
const deadline = Date.now() + 300_000;
let job;
while (Date.now() < deadline) {
const current = await api(`/jobs/${jobId}`);
job = current.data;
const failed = (job.tasks ?? []).filter((task) => task.status === "error");
if (failed.length) {
throw new Error(failed.map((task) => `${task.name}: ${task.message ?? "task failed"}`).join("; "));
}
if (job.status === "finished") break;
if (job.status === "error") throw new Error(`CloudConvert job failed: ${JSON.stringify(job)}`);
await new Promise((resolve) => setTimeout(resolve, 2000));
}
if (job?.status !== "finished") throw new Error(`Timed out waiting for job ${jobId}`);
const exportTask = job.tasks.find((task) => task.name === "export-screenshot");
const downloadUrl = exportTask?.result?.files?.[0]?.url;
if (!downloadUrl) throw new Error("Export task completed without a file URL");
const imageResponse = await fetch(downloadUrl);
if (!imageResponse.ok) throw new Error(`Download returned HTTP ${imageResponse.status}`);
await writeFile("screenshot.png", Buffer.from(await imageResponse.arrayBuffer()));
console.log("Saved screenshot.png");
5. Choose capture and output options
| Need | How to approach it |
|---|---|
| Lossless output | Use output_format: "png" for crisp text and interface details. |
| Compressed image | Use output_format: "jpg" when a lossy image format suits the destination. Check the operation reference for accepted values. |
| Full page | The capture API describes full-page capture as the default. |
| Specific viewport | Set viewport width and related sizing controls when the output should reflect a particular screen size. The API example includes width; consult the operation reference for supported controls. |
| Wait for page content | Use wait_for_element with a CSS selector when the desired content appears after initial navigation. The API example shows this as an optional setting. |
| Protected page | Supply custom authorization headers where appropriate. Keep credentials private and avoid logging them. |
| PDF output | The operation also supports PDF capture; choose the PDF output configuration documented for the operation rather than using the PNG example unchanged. |
Do not add optional settings without a capture requirement. A selector wait is useful when a known element marks readiness; a fixed delay may waste time or still finish before unpredictable content appears. Check CloudConvert’s operations reference for the available parameters and their accepted values.
6. Wait for completion and retrieve the file
CloudConvert jobs are asynchronous by default. The simple examples poll the job endpoint so they can demonstrate the full lifecycle in one script. For a production workflow, CloudConvert also documents webhook notifications, which let a worker react to completion without repeatedly polling. Its API overview describes a synchronous option for workflows that need an immediate result; choose that only when its request and response behavior fits your application.
Export URLs are temporary: CloudConvert’s quickstart says they remain valid for 24 hours. Download the result promptly and store it in your own durable storage if the application needs longer retention. Do not treat the temporary export URL as a permanent asset URL.
7. Region, reliability, and cost considerations
- Region: The API introduction documents automatic region selection and region-specific API endpoints for Germany (
eu-central) and Virginia, USA (us-east). Use the endpoint appropriate to your account and deployment requirements; the available facts do not establish broader data residency guarantees. - Retries: Treat network timeouts and HTTP 5xx responses as potentially transient, but first check whether a job was created before retrying job creation. Blindly resubmitting can create duplicate work. Use bounded retries with backoff, and retain the job ID for status checks.
- Rate limits: CloudConvert documents HTTP 429 responses and says some endpoints communicate rate-limit guidance in headers such as
Retry-After. Honor that value when present; there is no single universal limit established here. - Cost: CloudConvert’s product page displays a starting price of $0.008 per file. That is a vendor-published starting figure, not a quote for a particular screenshot configuration or volume. Check current plans and terms before estimating production cost.
- Performance: No broadly applicable capture-time benchmark is established by the cited documentation. Page weight, third-party resources, waits, and capture settings can affect an individual workflow; measure your own workload and set a client deadline that matches it.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| HTTP 401 or 403 | Missing, invalid, or insufficiently authorized credentials. | Check the Bearer token, account access, and that the key is sent server-side. Rotate a key if it may have been exposed. |
| HTTP 422 when creating a job | Invalid task structure, operation name, URL, or output option. | Confirm the operation is exactly capture-website, required fields are present, and task names referenced by input match. |
| Job is still processing | The capture task has not finished, or the page takes longer than expected. | Continue checking the job rather than trying to download before the export task completes. Apply a sensible overall deadline and inspect task status. |
| Job or task reports an error | The target could not be captured or the submitted options were rejected. | Read the task’s error message and validate the URL and optional settings. Avoid repeating an unchanged request without addressing the reported cause. |
| HTTP 429 | Rate limit reached. | Wait for the indicated interval when Retry-After is present, then retry with bounded backoff. |
| HTTP 500 or 503 | Server-side failure or temporary service unavailability. | Record the response and job ID, retry with backoff where safe, and check job state before resubmitting creation. |
| Downloaded URL no longer works | The temporary export URL expired. | Run or locate the job again if possible, and download completed exports within the documented 24-hour validity period. |
| Screenshot is missing late content | The page had not rendered the target content when captured. | Wait for a meaningful selector with wait_for_element or use another documented readiness setting suited to the page. |
CloudConvert’s API introduction documents errors including 422, 500, 503, and 429, along with rate-limit headers on some endpoints: API introduction.
9. Or skip the browser setup
If you need a screenshot API without building the CloudConvert job and export flow, ScreenshotNeo takes a URL in one GET request and returns an image or PDF. See the ScreenshotNeo API documentation for its options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed; the response includes page-verdict and billing headers. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Sign up free for 1,000 screenshots a month, with no card required.
10. FAQ
Can I use the capture API for a page behind authentication?
The API describes custom authorization headers for protected pages. Send only credentials needed for the capture and keep them out of client-side code and logs.
Should I use PNG or JPG?
Choose PNG when preserving sharp text and edges matters; choose JPG when a compressed, lossy image is suitable. The output format is an explicit capture setting.
Can I use the export URL as permanent storage?
No. The quickstart documents 24-hour validity. Download the image and place it in storage you control if it must remain available longer.
Does this example prove a screenshot will finish within five minutes?
No. The scripts use a five-minute client-side deadline to avoid waiting forever. It is an example timeout, not a CloudConvert service guarantee or performance benchmark.


