# Save a webpage as PDF (browser, command line, API)

Save a webpage as PDF in Chrome, Edge, Firefox and Safari, from the command line with headless Chrome, or in bulk with one API call per URL.

**Quick answer.** For one page, press Ctrl+P (Cmd+P on Mac) and choose **Save as PDF**. To save pages from a script or app, send the URL to an API.

```bash
curl -X POST https://html-pdf.com/v1/pdf \
  -H "Authorization: Bearer $HTML_PDF_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}' \
  -o page.pdf
```

There are three ways to save a webpage as a PDF. Which one fits depends on how many pages you have and whether a person or a program is doing it.

For one page, use your browser's print dialog. For a few pages in a script, use headless Chrome. For many pages, or from an app or AI agent, use an API. All three use the browser's print engine, so the results look alike.

## In your browser

This section walks you through the print dialog in each major browser.

### Chrome

1. Open the page and press **Ctrl+P** (Windows, Linux) or **Cmd+P** (Mac).
2. Set **Destination** to **Save as PDF**.
3. Under **More settings**, choose paper size, margins and scale. Tick **Background graphics** to keep colors and images, and untick **Headers and footers** to remove the date, title and URL.
4. Click **Save**.

### Microsoft Edge

Press **Ctrl+P**, set **Printer** to **Save as PDF**, adjust layout and margins (background graphics are under **More settings**), then **Save**.

### Firefox

Press **Ctrl+P** or **Cmd+P**, set **Destination** to **Save to PDF**, then **Save**. Background colors and headers/footers are under **More settings**.

### Safari (Mac)

Choose **File > Export as PDF**, pick a name and location, and save. For control over paper size and margins, use **File > Print** and pick **Save as PDF** from the **PDF** menu at the bottom of the dialog.

### Tips for cleaner PDFs

- **Use reader mode first** (Firefox Reader View, Safari Reader) on articles to drop navigation and ads.
- **Scroll to the bottom before printing** on pages that lazy-load images.
- **The page's print stylesheet decides the result.** Many sites hide menus and change layout for print. If you want the page exactly as it looks on screen, use the "screen" media option described below.

## From the command line with headless Chrome

Chrome can print a page to PDF without opening a window.

```bash
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --headless --print-to-pdf=page.pdf --no-pdf-header-footer https://example.com

# Linux
google-chrome --headless --print-to-pdf=page.pdf --no-pdf-header-footer https://example.com
```

`--no-pdf-header-footer` removes the date and URL lines. Edge and Chromium accept the same flags.

The CLI is great for quick jobs, but it has few options. It can't wait for JavaScript content or add custom footers, and you only get print media. For more control you move to Puppeteer or Playwright. The full flag list is in [Chrome headless print to PDF](https://html-pdf.com/headless-chrome-print-to-pdf).

## With an API

An API call does the same thing from any language, without installing a browser. Send the URL and choose screen styles so the PDF matches what you see in the browser.

```bash
curl -X POST https://html-pdf.com/v1/pdf \
  -H "Authorization: Bearer $HTML_PDF_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "media": "screen", "format": "A4", "margin": "10mm"}' \
  -o page.pdf
```

These options are the most useful for webpages.

- `"media": "screen"` uses the site's screen CSS. `"print"` (the default) uses its print stylesheet.
- `"landscape": true` for wide pages and dashboards.
- `"wait_delay": 2` waits up to 10 seconds for late content, and `"wait_for_expression"` waits for a JavaScript condition.
- `"footer_html"` adds page numbers (leave a bottom margin for it).
- `"max_pages": 20` fails very long pages instead of charging you for them.
- `"response": "url"` returns a download link valid for 24 hours instead of the file.

More examples in Python and Node.js on the [URL to PDF API](https://html-pdf.com/url-to-pdf-api) page.

## Save many webpages as PDF

This section shows how to convert a whole list of URLs. Put one URL per line in `urls.txt` and loop over it. This Bash script names each file after the URL and keeps going if one page fails.

```bash
#!/usr/bin/env bash
# save-pages.sh: usage: HTML_PDF_KEY=... ./save-pages.sh urls.txt
set -u
mkdir -p pdfs
while IFS= read -r url; do
  [ -z "$url" ] && continue
  name=$(printf '%s' "$url" | sed -E 's#^https?://##; s#[^A-Za-z0-9._-]+#_#g' | cut -c1-120)
  code=$(curl -s -o "pdfs/$name.pdf" -w '%{http_code}' -X POST https://html-pdf.com/v1/pdf \
    -H "Authorization: Bearer $HTML_PDF_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"$url\", \"media\": \"screen\", \"max_pages\": 30}")
  if [ "$code" = "200" ]; then
    echo "ok    $url"
  else
    echo "fail  $url ($code)"; rm -f "pdfs/$name.pdf"
  fi
  sleep 0.5   # stay well under 120 requests per minute
done < "$1"
```

Here is the same in Python, if you prefer.

```python
import os, re, requests

os.makedirs("pdfs", exist_ok=True)
for url in filter(None, map(str.strip, open("urls.txt"))):
    r = requests.post("https://html-pdf.com/v1/pdf",
        headers={"Authorization": f"Bearer {os.environ['HTML_PDF_KEY']}"},
        json={"url": url, "media": "screen", "max_pages": 30}, timeout=60)
    name = re.sub(r"[^A-Za-z0-9._-]+", "_", re.sub(r"^https?://", "", url))[:120]
    if r.ok:
        open(f"pdfs/{name}.pdf", "wb").write(r.content)
        print("ok  ", url)
    else:
        print("fail", url, r.status_code, r.text[:200])
```

Failed renders don't use credits, so a bad URL in the list costs nothing.

## Pages behind a login

This section covers pages the API can't reach on its own. The API sees each page as an anonymous visitor would, and it only reaches public addresses.

For dashboards, account pages or anything that needs a session, get the HTML yourself. Take it from your server's templates, or capture it after logging in with your own tooling, and send it as `html` instead of `url`.

```json
{ "html": "<!doctype html><html>…the rendered page…</html>", "media": "screen" }
```

Make image and stylesheet links absolute (`https://...`), inline them, or upload them alongside `index.html` as a multipart request. In the browser, of course, the print dialog works on any page you're logged into.

## From an AI agent

You can also let an AI assistant do it for you. Connect Claude, ChatGPT, Cursor or another MCP client to the [PDF MCP server](https://html-pdf.com/pdf-mcp-server) and ask *"Save https://example.com/pricing as a landscape PDF."* The agent calls `url_to_pdf` and returns a link.

## Pricing

Here is what the API costs. 1 credit = 1 PDF page. You get 100 free credits once, then $10 for 1,000 pages down to $0.005 per page with the $250 pack.

There's no subscription, and purchased credits never expire.

```bash
curl -X POST https://html-pdf.com/v1/signup -H "Content-Type: application/json" -d '{"email": "you@example.com"}'
# Then open the link we email you to confirm (adds the 100 free credits)
```

[Get a free API key](https://html-pdf.com/login) · [API docs](https://html-pdf.com/docs) · [Guides](https://html-pdf.com/guides)

## FAQ

### How do I save a webpage as a PDF in Chrome?

Press Ctrl+P (Cmd+P on Mac), set **Destination** to **Save as PDF**, and click **Save**. Under **More settings**, tick **Background graphics** to keep colors and images.

### How do I save a webpage as PDF without the date and URL at the top?

In the browser print dialog, untick **Headers and footers**. With headless Chrome, add the `--no-pdf-header-footer` flag.

### How can I save many webpages as PDFs at once?

Put the URLs in a text file and loop over them, sending each one to `POST https://html-pdf.com/v1/pdf` with `{"url":"https://..."}`. Failed renders cost nothing, so a broken URL in the list doesn't use credits.

### Why does my saved PDF look different from the webpage?

Browsers print with the site's print stylesheet, which often hides menus and changes the layout. With the API, send `"media": "screen"` to keep the on-screen look.
