# Capture screenshots and PDFs

> Capture a page as a PNG, JPEG or WebP screenshot with screenshot=true, or print it to PDF with response_format=pdf on the chromium engine.

Source: https://docs.spicrawl.com/guides/screenshots-and-pdf

Use this when you need the page as it looks rather than as it reads: visual regression checks, archiving a page as evidence, a thumbnail for a link preview, or a printable PDF of an invoice or report. Set `screenshot: true` for an image, or `response_format: "pdf"` for a document.

Both need an engine with a rasteriser. Screenshots and PDFs work on `chromium` (`engine: "chromium"`). The fetch engine, `obscura` (what `js_render: true` selects) and `mode: "auto"` cannot draw a page, and the request is refused before anything is spent.

## Minimal request

```bash title="curl"
curl https://api.spicrawl.com/v1/scrape \
  -H "Authorization: Bearer $SPICRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/products/42", "engine": "chromium", "screenshot": true, "screenshot_fullpage": true}'
```

```python title="Python"
import base64, os, requests

r = requests.post(
    "https://api.spicrawl.com/v1/scrape",
    headers={"Authorization": f"Bearer {os.environ['SPICRAWL_API_KEY']}"},
    json={"url": "https://example.com/products/42", "engine": "chromium",
          "screenshot": True, "screenshot_fullpage": True},
    timeout=120,
)
r.raise_for_status()
for shot in r.json().get("screenshots", []):
    with open(f"{shot['label']}.{shot.get('format', 'png')}", "wb") as f:
        f.write(base64.b64decode(shot["data"]))
```

```typescript title="TypeScript"
import { writeFileSync } from "node:fs";

const r = await fetch("https://api.spicrawl.com/v1/scrape", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SPICRAWL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/products/42",
    engine: "chromium",
    screenshot: true,
    screenshot_fullpage: true,
  }),
});
if (!r.ok) throw new Error(JSON.stringify(await r.json()));
const env = await r.json();
for (const shot of env.screenshots ?? []) {
  writeFileSync(`${shot.label}.${shot.format ?? "png"}`, Buffer.from(shot.data, "base64"));
}
```

```bash title="CLI"
spicrawl scrape https://example.com/products/42 --engine chromium \
  --screenshot --screenshot-full-page -o product.html
# saves product.html plus product.final.png next to it
```

## What comes back

`screenshot: true` always returns the JSON envelope. The rendered page is under `content` and each image is an object in `screenshots`:

```json
{
  "url": "https://example.com/products/42",
  "final_url": "https://example.com/products/42",
  "status": 200,
  "content": "<!doctype html><html>…",
  "engine": "chromium",
  "credits": 8,
  "screenshots": [
    {
      "label": "final",
      "encoding": "base64",
      "data": "iVBORw0KGgoAAAANSUhEUgAA…",
      "size_bytes": 184223,
      "format": "png",
      "width": 800,
      "height": 3412
    }
  ],
  "warnings": []
}
```

* The top-level screenshot has `label: "final"`. Screenshots taken by a `screenshot` [browser action](https://docs.spicrawl.com/guides/browser-actions.md) carry that step's label.
* `width` and `height` are omitted when the engine did not measure them.
* If you asked for another `response_format` (for example `markdown`), the response is still the envelope, with `content` in the format you asked for, and `X-Warning: FORMAT_COERCED`.
* `screenshots` lists delivered images only. An image the engine failed to deliver is reported in `warnings`, and the request is charged 0 credits even though the page comes back.

With `response_format: "pdf"`, the body is the PDF bytes (`Content-Type: application/pdf`), not the envelope. Save it as a file. Setting `screenshot`, `extract`, `autoparse`, `links`, `ai_extract` or `network_capture` at the same time switches the response to the envelope with `X-Warning: FORMAT_COERCED`, so leave them off for a PDF.

```bash title="curl"
curl https://api.spicrawl.com/v1/scrape \
  -H "Authorization: Bearer $SPICRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/invoices/2026-09", "engine": "chromium", "response_format": "pdf"}' \
  -o invoice.pdf
```

```bash title="CLI"
spicrawl scrape https://example.com/invoices/2026-09 --engine chromium --format pdf -o invoice.pdf
```

The CLI refuses to write PDF bytes to a terminal: pass `-o FILE`, or `--json` to get the output base64-encoded.

## Options

| Field                 | Default                       | Effect                                                                                                                                 |
| --------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `screenshot`          | `false`                       | Capture an image. Forces the JSON envelope.                                                                                            |
| `screenshot_fullpage` | `false`                       | Capture the whole scrollable page instead of the viewport. Requires `screenshot: true`. Cannot be combined with `screenshot_selector`. |
| `screenshot_selector` | none                          | CSS selector of one element to capture. Requires `screenshot: true`. Cannot be combined with `screenshot_fullpage`.                    |
| `screenshot_format`   | engine default (`png`)        | `png`, `jpeg`, `jpg` or `webp`.                                                                                                        |
| `screenshot_quality`  | `0` (engine default)          | 0–100, used by `jpeg` and `webp`. A value above 0 requires `screenshot: true`.                                                         |
| `response_format`     | `html`                        | `pdf` returns a browser-printed PDF. Needs `engine: "chromium"`.                                                                       |
| `headless`            | deployment default (headless) | `false` runs Chromium with a real display: a 1920x1080 screen instead of 800x600. Chromium only.                                       |
| `wait`, `wait_for`    | none                          | Let late content load before capture. See [JavaScript rendering](https://docs.spicrawl.com/guides/javascript-rendering.md).                                        |
| `block_resources`     | see below                     | Resource classes the browser does not load.                                                                                            |

**`screenshot_format` is not validated by the API.** An unrecognised value (for example `"gif"`) is not a 400: the engine silently uses its default, PNG. Check the `format` field on each returned screenshot to see what you got.

**Images and fonts load by default here.** Browser renders normally block images and fonts. That default is skipped when you set `screenshot` or `response_format: "pdf"`, because a capture without them would not show the real page. An explicit `block_resources` list still applies, so leave it unset unless you want those resources missing from the capture.

## Failure modes

| Code                                   | HTTP | What to do                                                                                                                                       |
| -------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ERR::REQUEST::CAPABILITY_UNSUPPORTED` | 400  | The selected engine cannot rasterise: fetch, `obscura`, `js_render: true` without a pin, or `mode: "auto"`. Set `engine: "chromium"`. 0 credits. |
| `ERR::REQUEST::INCOMPATIBLE_FLAGS`     | 400  | `screenshot_fullpage` or `screenshot_selector` without `screenshot: true`, or both at once.                                                      |
| `ERR::AUTH::ENGINE_NOT_ENTITLED`       | 403  | Your plan does not include `chromium`. Upgrade to a plan that includes it. 0 credits.                                                            |
| `ERR::ENGINE::UNAVAILABLE`             | 503  | The engine is not deployed or has no free slot. Retry after `Retry-After`.                                                                       |
| `ERR::ENGINE::RENDER_FAILED`           | 502  | The page could not be rendered. Retry once; if it repeats, try `wait_for` on a stable selector.                                                  |

A missing element for `screenshot_selector`, or any other capture the engine could not deliver, is not an error: the page is returned, the image is absent from `screenshots`, `warnings` says why, and the charge is 0.

## Cost

A screenshot or PDF adds no credits on top of the engine price. You pay for the engine that ran: `chromium` is 8 credits.

Failures, undelivered screenshots and non-billable target statuses cost 0. A repeat of the same request inside the cache window is served from the [cache](https://docs.spicrawl.com/guides/caching.md) at 0 credits, image included. `X-Credits-Charged` is what was billed. See [Credits](https://docs.spicrawl.com/credits.md).

## Related

* [Browser actions](https://docs.spicrawl.com/guides/browser-actions.md) to screenshot the page between steps
* [JavaScript rendering](https://docs.spicrawl.com/guides/javascript-rendering.md) for `wait` and `wait_for`
* [Anti-bot](https://docs.spicrawl.com/guides/anti-bot.md) when the page behind the screenshot is protected
* [Caching](https://docs.spicrawl.com/guides/caching.md)
