From request
to PNG.
The complete HTTP contract for website captures. Use a paid workspace API key from your server; examples below target the Hostinger Node.js release.
1. Authentication
In a verified, paid workspace, the owner opens Developer API and creates a key. Copy it once into your backend’s secret store. Send Authorization: Bearer YOUR_API_KEY on create, status and result requests. Keys are revocable from the same screen. Revocation stops future authenticated access.
Base URL: https://run.goclick.it. Bearer requests do not use browser session cookies or the app’s CSRF token. Browser account routes have separate authentication rules.
2. Create a screenshot
POST /api?r=api-capture · Content-Type: application/json
curl --request POST 'https://run.goclick.it/api?r=api-capture' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{"url":"https://example.com","width":1440,"height":900,"mode":"full"}'Successful response · HTTP 202
{
"id": "JOB_ID",
"status": "done",
"status_url": "/api?r=job&id=JOB_ID"
}The current shared-hosting release waits for a bounded capture attempt and returns after completion. HTTP 202 retains compatibility with the job API; it does not promise a persistent background queue. The provider timeout is 40 seconds, so allow a longer client timeout and handle network failures. The complete HTTP request can take longer than the provider timeout.
3. Parameters and capture modes
| Field | Type / default | Meaning |
|---|---|---|
| url | String · required | Public HTTP/HTTPS URL, up to 2,048 characters. No embedded credentials or private-network addresses. Standard ports only. |
| width | Integer · 1440 | Viewport width: 320–2560 pixels. |
| height | Integer · 900 | Custom viewport height: 200–2000 pixels; section height is controlled separately. |
| mode | String · viewport | viewport, full, selector, offset or section. |
| selector | String · conditional | Required for selector mode; a CSS selector up to 300 characters. |
| offset | Integer · 0 | Vertical position for offset mode: 0–15000 pixels. |
Viewport
{"url":"https://example.com","mode":"viewport","width":1440,"height":900}Full page
{"url":"https://example.com","mode":"full","width":1440,"height":900}CSS section
{"url":"https://example.com","mode":"selector","selector":"main","width":1440,"height":900}Vertical offset
{"url":"https://example.com","mode":"offset","offset":600,"width":1440,"height":900}Device sizing is viewport emulation. Page appearance comes from the website’s default styling. Private-network URLs, authenticated browser sessions and forced color-scheme rendering are not supported in this API release.
4. Read status and download the PNG
curl 'https://run.goclick.it/api?r=job&id=JOB_ID' \
--header 'Authorization: Bearer YOUR_API_KEY'{
"id": "JOB_ID",
"status": "done",
"error": null,
"result_url": "/api?r=job-result&id=JOB_ID"
}curl 'https://run.goclick.it/api?r=job-result&id=JOB_ID' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--output screenshot.pngStatus values are queued, running, done and failed. Fetch results only after done. An existing running job can be polled, but new captures normally complete in their create request. Results are temporary for one hour. A workspace key cannot retrieve another workspace’s jobs.
5. Server-side JavaScript example
// Node.js 22+. Keep GOCLICKIT_API_KEY in server environment variables.
import { writeFile } from 'node:fs/promises';
const base = 'https://run.goclick.it';
const headers = { Authorization: 'Bearer ' + process.env.GOCLICKIT_API_KEY };
async function readJSON(response) {
const body = await response.json();
if (!response.ok) throw new Error(body.error || 'Capture request failed');
return body;
}
const job = await readJSON(await fetch(base + '/api?r=api-capture', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com', mode: 'full' }),
signal: AbortSignal.timeout(90000)
}));
let status;
for (let attempt = 0; attempt < 30; attempt++) {
status = await readJSON(await fetch(new URL(job.status_url, base), {
headers, signal: AbortSignal.timeout(15000)
}));
if (status.status === 'done') break;
if (status.status === 'failed') throw new Error(status.error);
await new Promise(resolve => setTimeout(resolve, 2000));
}
if (status?.status !== 'done') throw new Error('Capture did not complete');
const image = await fetch(new URL(status.result_url, base), {
headers, signal: AbortSignal.timeout(30000)
});
if (!image.ok) throw new Error('Result could not be downloaded');
await writeFile('screenshot.png', Buffer.from(await image.arrayBuffer()));6. Error responses
{"error":"A readable explanation of the failure."}| Status | Meaning | Action |
|---|---|---|
| 400 | Invalid URL, dimensions, selector or request JSON | Validate the request before submitting again. |
| 401 | Missing, invalid or revoked Bearer key | Check the active key. |
| 403 | No active paid workspace or access denied | Check the workspace plan and key ownership. |
| 404 | Job expired or is unavailable to this workspace | Check the ID and one-hour expiry. |
| 409 | Result is not ready | Read the status before fetching the PNG. |
| 429 | Capture rate or monthly render quota reached | Slow requests or wait for quota renewal/upgrade. |
| 502 | Website capture/provider failure | Inspect the website and retry deliberately. |
| 503 | Service or account integration is not configured/available | Wait or contact support. |
There is no idempotency-key contract in this release. Do not blindly retry a timed-out create request: the server may already have completed and consumed a render. Failed provider jobs refund their reservation once; a lost client response is a different condition.
7. Quotas, results and privacy
Pro: 500 renders/month. Team: 2,000 renders/month. API and app renders share that allowance. Capture submissions are limited to 15 per minute per workspace. Local image editing and extension pixel captures do not use render credits. Provider output is limited to 20 MB; PNG dimensions are checked against a 40-megapixel bound.
Signed direct image URLs are documented below. Separate API credit packs, webhook callbacks and independent scheduled jobs are not available yet. Keys and source URLs must not contain secrets in logs or public code. Review plans and planned capabilities.
8. Domain whitelists
Each API key requires allowed target domains. The workspace owner can add or edit them in the app’s Developer API page. Use exact hosts such as example.com, or *.example.org for subdomains only. A wildcard does not include the apex host: list example.org separately if needed. Schemes, paths, ports, IP literals and a global * are rejected. A domain whitelist is a capture policy, not a claim of domain ownership.
The check applies to the requested website URL before a new API render is reserved. It is not an assertion about every third-party resource or subsequent navigation loaded by the target website. Existing keys with no whitelist must be configured before submitting new captures.
9. Screen types, offsets and height-based sections
| device | Viewport | Behavior |
|---|---|---|
| desktop | 1440 × 900 | Desktop viewport |
| tablet | 768 × 1024 | Touch/mobile viewport emulation |
| mobile | 390 × 844 | Touch/mobile viewport emulation |
| custom | width × height | 320–2560 wide, 200–2000 high |
If device is omitted and width/height are supplied, custom sizing is used for compatibility. Otherwise desktop is the default. Named presets determine their own dimensions; width/height overrides apply only to custom.
{"url":"https://example.com","device":"mobile","mode":"section","offset":300,"section_height":600}mode=section captures the chosen pixel height from a vertical offset. Section height is 1–10000 pixels, with an area of at most 16 megapixels; offset is 0–15000 pixels. mode=offset uses the viewport height at the offset. Full-page and CSS-selector modes must use offset zero; combine a nonzero offset with section or offset mode. These are screenshot coordinates, not a real-device test service or a guarantee of scroll-triggered application behavior.
10. Direct screenshot image URLs
Direct URLs return PNG bytes for use in an image embed, report or thumbnail. They contain a signature that binds the target URL, device, dimensions, mode, offset, section height, selector, expiry and cache lifetime. Editing a parameter invalidates the signature. Your full API key is never placed in the image URL.
- As a paid workspace owner, open Developer API and create a screenshot URL profile.
- Set mandatory allowed target domains and optional embedding hosts.
- Choose capture settings, link lifetime and render-cache lifetime, then generate and copy the signed URL.
- Use it as the image source. Generate a new signed link when you change settings or when the link expires.
<img src="https://run.goclick.it/shot/PROFILE_ID.png?url=https%3A%2F%2Fexample.com&device=mobile&mode=section&offset=300§ion_height=600&expires=UNIX_TIMESTAMP&cache=3600&signature=GENERATED_SIGNATURE" alt="Website screenshot">This URL is illustrative. Use the generator to obtain a valid signature and timestamp. A direct URL is a shareable image-access capability; it is not a private cloud-media permission link. Do not include secrets in target URLs, page content, embeds or logs.
11. Generate a signed URL from your backend
POST /api?r=sign-screenshot-url with your Bearer key. Create the profile in the owner UI first, then use its displayed profile ID. Both the API key’s target whitelist and the profile’s whitelist must allow the target.
{
"id": "PROFILE_ID",
"url": "https://example.com",
"device": "custom",
"width": 1280,
"height": 800,
"mode": "section",
"offset": 400,
"section_height": 500,
"ttl": 86400,
"cache": 3600
}Successful JSON response contains url, expires_at and cache_seconds. Link lifetime (ttl) is 60 seconds to 30 days; default 24 hours. Render-cache lifetime (cache) is 60–3600 seconds; default one hour. Generating a signature does not create a render. Signing is limited to 60 requests/minute/workspace.
12. Cache, credits, access and revocation
The first GET for an uncached signed capture uses one workspace render. Cache hits reuse the existing image without a new render credit. After cache expiry, the next GET attempts a fresh render. There is no independent scheduled refresh. Capture submissions remain limited to 15/minute/workspace, and direct URL requests to 120/minute/profile; monthly paid render limits still apply.
Concurrent cache misses may return HTTP 503 with Retry-After while the existing request renders. HEAD returns cached metadata only; an uncached HEAD returns 409 and does not create a render. Link expiry, profile revocation, profile whitelist changes or loss of a paid plan are checked before serving a cached image. Failed renders refund their reserved credit.
Optional embedding-host rules check Referer/Origin and reject missing headers. Browser referrer policies, image proxies and email clients can suppress these headers. They are a hotlink deterrent, not authentication: non-browser clients can spoof them. The signed link and target whitelist are the primary controls. API key revocation stops future signing/captures with that key; revoke the URL profile to stop already-issued links. Revocation cannot recall downloaded copies.
Image responses use no-store caching at the browser/proxy layer so server access checks remain effective. Cache reuse is internal to GoClickIT and its private R2 storage; no public R2 bucket is required. Existing capture results expire after one hour, which bounds cache lifetime.
Frequently asked questions
Do result URLs work without authentication?
No. The result route requires the same active workspace authorization. To share a saved capture, use the app’s media upload and sharing workflow.
Does GET status create another render?
No. A new capture submission reserves a render; reading status and downloading that existing result do not submit another capture.