# A URL to PDF API for your own code

> A URL to PDF API for developers: send a page address with format=pdf, get a PDF back. Paper size, margins, print styles, webhooks and signed links.

CurlShot is a URL to PDF API. Send the address of a web page with format=pdf and the answer is a PDF of that page, printed by a real browser: text you can select and search, links that work, and your choice of paper size, margins and orientation. It works from any language that can make an HTTP request, and long jobs can run in the background and report to a webhook.

Page for people: https://curlshot.com/tools/url-to-pdf-api

![The first page of a PDF made from the Wikipedia article about the Eiffel Tower](https://curlshot.com/docs/examples/pdf-page.webp "Page one of a URL turned into an A4 PDF by the API. The text stays selectable.")

## How to convert a URL to PDF with an API in three steps

1. Create a free account and copy your access key.
2. Send the page address with format=pdf, and the paper size and margins you want.
3. Store the PDF that comes back, or let a webhook tell you when it is ready.

## PDF options for a URL, with their defaults

Every PDF option is a parameter of the same request. Leave them all out and you get A4 pages, no margin, backgrounds printed and the screen styles of the page. A PDF always covers the whole page, so full_page is not needed.

| Option | Default | What it changes |
| --- | --- | --- |
| pdf_paper_format | a4 | Paper size: a0, a1, a2, a3, a4, a5, a6, letter, legal, tabloid, ledger |
| pdf_landscape | false | true turns the paper on its side, for wide tables and dashboards |
| pdf_margin | none | One margin for all four sides: 10mm, 1cm, 0.5in or 20px |
| pdf_margin_top, pdf_margin_right, pdf_margin_bottom, pdf_margin_left | none | One side each; wins over pdf_margin |
| pdf_print_background | true | false drops background colours and images, for a printer |
| pdf_fit_one_page | false | true puts the whole page on one sheet as tall as the content |
| media_type | screen | print uses the stylesheet the site wrote for paper |

- selector, clip and the image options have no effect on a PDF; selector and clip are refused with format=pdf.
- With pdf_fit_one_page the width of the sheet follows viewport_width, so a 1280 px window gives a 1280 px wide page.
- With response_type=json, width and height describe one page of the PDF, in pixels at 96 per inch.

## A clean PDF of a page you do not control

Your own pages print the way you designed them. Other people's pages arrive with a consent pop-up, a sticky header that repeats at the top of every sheet, and content that loads late. Each has an option.

| What goes wrong | Option that fixes it |
| --- | --- |
| A cookie banner over the first page | block_cookie_banners=true |
| Ads and a chat bubble on every sheet | block_ads=true, block_chats=true |
| A sticky header repeated down the document | hide_selectors with its CSS selector, or media_type=print |
| Charts or prices missing, because they load after the page | wait_until=networkidle, or wait_for_selector with the element you need |
| Images further down left blank | Nothing: the page is scrolled before printing so they load (full_page_scroll) |
| The menu and sidebar take half the first page | media_type=print, or hide them with hide_selectors |
| The text breaks inside a table row | pdf_fit_one_page=true for a single long sheet |

## Many PDFs, or PDFs that take a while

A long page can take several seconds to load and print. Rather than hold a request open, add async=true: the API answers at once with a job, renders in the background, and posts the result to your webhook_url when it is done. The webhook body carries a link to the PDF, signed so your server can check it came from the API.

For a list of pages, a bulk call takes up to 100 at once, each with its own options, on plans that include bulk.

- The webhook arrives once the job is done or has failed. Download the PDF when it arrives: the link in it expires.
- Checking a job's status is free and does not use your quota.

Node.js: start a PDF job with a webhook:

```javascript
const response = await fetch('https://curlshot.com/api/v1/screenshot', {
  method: 'POST',
  headers: { 'X-Access-Key': process.env.SCREENSHOT_ACCESS_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/report',
    format: 'pdf',
    pdf_paper_format: 'a4',
    pdf_margin: '15mm',
    async: true,
    webhook_url: 'https://your-app.example/hooks/pdf',
  }),
})
const job = await response.json()
if (!response.ok) throw new Error(job.error_code + ': ' + job.error_message)
console.log('Started', job.job_id)
```

Python: the same PDF, waited for in the request:

```python
import os

import requests

response = requests.get(
    "https://curlshot.com/api/v1/screenshot",
    headers={"X-Access-Key": os.environ["SCREENSHOT_ACCESS_KEY"]},
    params={"url": "https://example.com/report", "format": "pdf", "pdf_paper_format": "a4", "pdf_margin": "15mm"},
    timeout=120,
)
response.raise_for_status()
with open("report.pdf", "wb") as f:
    f.write(response.content)
```

## A "Download as PDF" link on your own site

To let visitors download a PDF of a page, do not put your access key in the page. Make a signed link on your server instead: the request address plus a signature made with your secret key. Nobody can change the page or the options in it, and an expires parameter can give the link a lifetime.

Add cache=true so that a hundred clicks on the same link make one PDF, not a hundred. Cache hits are not counted against your allowance.

- Signing takes a few lines in any language; the docs have the code for six.

A signed PDF link in a page:

```html
<a href="https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&cache=true&format=pdf&media_type=print&url=https%3A%2F%2Fexample.com%2Finvoice%2F1042&signature=SIGNATURE">
  Download as PDF
</a>
```

- [How to sign a link](https://curlshot.com/docs/signed-links)
- [Save a single page as PDF, no code](https://curlshot.com/tools/website-to-pdf)
- [PDF from your own HTML instead of a URL](https://curlshot.com/tools/html-to-pdf)

## What people use it for

- Archive pages of your own app or of a site you track, as dated documents
- Attach the current state of a dashboard or report to an email
- Offer a "Download as PDF" link on pages your users can already see
- Keep records of terms, prices or listings at a moment in time

## The same thing with the API

```bash
curl "https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://example.com&format=pdf&pdf_paper_format=letter&pdf_margin=15mm&block_cookie_banners=true" \
  --output page.pdf
```

An access key is free: https://curlshot.com/register. Read more:

- [PDF rendering](https://curlshot.com/docs/pdf.md)
- [Async and webhooks](https://curlshot.com/docs/async-and-webhooks.md)
- [Signed links](https://curlshot.com/docs/signed-links.md)

## Questions

### How do I convert a URL to PDF with an API?

Send a GET request to the CurlShot screenshot endpoint with your access key, the page address in url, and format=pdf. The body of the answer is the PDF file, with Content-Type application/pdf.

### Is the text in the PDF real text?

Yes. The PDF is printed by the browser, not built from a screenshot, so the text can be selected, searched and copied, links stay clickable, and the file stays small compared with an image of the same page.

### Does a long page cost more than a short one?

No. One successful render counts as one, whether the PDF has one page or forty. Failed renders are not counted, and a repeat served from the cache is free.

### Can it make a PDF of a page that needs a login?

Yes, when you pass what the page needs: a session cookie with cookies, extra headers with headers, or an Authorization header with authorization, which is sent to the target site only. Pages on a private network are refused; send their HTML instead.

### How is this different from the web page to PDF tool?

It is the same engine. The tool is a page where a person pastes an address and downloads one PDF. The API is for code: the same result from a request, with every PDF option, background jobs, webhooks and bulk calls.

## Related

- [Web page to PDF](https://curlshot.com/tools/website-to-pdf.md): Turn a web page into a PDF document from its address. Real pages, working links and selectable text. Paste the address, nothing to install. Also an API.
- [HTML to PDF](https://curlshot.com/tools/html-to-pdf.md): An HTML to PDF API: POST your HTML and CSS, get a PDF back. A4, Letter and 9 other paper sizes, margins, page breaks. Node, Python, PHP, cURL. Free plan.
- [Bulk screenshots](https://curlshot.com/tools/bulk-screenshots.md): Screenshot a whole list of web pages at once. Start up to 100 screenshots with one API call, then collect the results by webhook or one status request.

## Guides

- [Web page to PDF: paper sizes, margins, print CSS](https://curlshot.com/blog/web-page-to-pdf-paper-sizes.md): Paper sizes in millimetres, inches, CSS pixels and points, how margins and page breaks work in a PDF of a web page, and the print CSS that controls them.
- [Capture a web page as evidence that holds up](https://curlshot.com/blog/web-page-as-evidence.md): A checklist for saving a web page as a credible record: what to capture, how to fix the time with a hash and timestamp, and what makes a screenshot weak.
