PDFPipe

Guide

Add page numbers to a PDF

Pass header_html or footer_html in your render options to stamp a header or footer on every page of the generated PDF. The renderer substitutes special class names with live values: current page, total pages, date, title, and URL.

The minimal example

Add a centered page number to every page. The .pageNumber span is replaced by the renderer with the current page number on each page.

javascript
const res = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.PDFPIPE_KEY}`,
  },
  body: JSON.stringify({
    html: "<h1>My Report</h1><p>Content here...</p>",
    options: {
      format: "A4",
      footer_html: `
        <div style="
          font-family: sans-serif;
          font-size: 10px;
          color: #888;
          text-align: center;
          padding: 0 1cm;
          width: 100%;
        ">
          Page <span class="pageNumber"></span>
        </div>
      `,
    },
  }),
});

Available substitution classes

Use these class names anywhere inside your header or footer HTML. The renderer replaces the content of each element with the live value for that page.

ClassReplaced with
.pageNumberCurrent page number (1-based)
.totalPagesTotal number of pages in the document
.dateCurrent date at render time
.titleThe <title> element of the rendered HTML
.urlThe URL of the rendered page (for URL renders)

Header and footer together

Use both at once. A common pattern for reports: document title in the header, page counter in the footer.

javascript
const HEADER = `
  <div style="
    font-family: Georgia, serif;
    font-size: 9px;
    color: #555;
    display: flex;
    justify-content: space-between;
    padding: 0 1cm;
    width: 100%;
    border-bottom: 1px solid #ddd;
  ">
    <span>Acme Corp: Q2 2026 Report</span>
    <span class="date"></span>
  </div>
`;

const FOOTER = `
  <div style="
    font-family: Georgia, serif;
    font-size: 9px;
    color: #999;
    text-align: right;
    padding: 0 1cm;
    width: 100%;
  ">
    Page <span class="pageNumber"></span> of <span class="totalPages"></span>
  </div>
`;

const res = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.PDFPIPE_KEY}`,
  },
  body: JSON.stringify({
    html: reportHtml,
    options: {
      format: "A4",
      margin: "2cm",  // give headers/footers room
      header_html: HEADER,
      footer_html: FOOTER,
    },
  }),
});

Margin tip

The renderer automatically widens the top and bottom margins from 1cm to 2cm when you use the default 1cm margin, so the header and footer do not overlap your content. If you set a custom margin, set it large enough for your header or footer height. A 40 to 50 px header comfortably fits inside 2cm of space.

Python example

python
import os, requests

FOOTER = """
<div style="font-family:sans-serif;font-size:10px;color:#999;
            text-align:right;padding:0 1cm;width:100%">
  Page <span class="pageNumber"></span> / <span class="totalPages"></span>
</div>
"""

resp = requests.post(
    "https://api.pdfpipe.xyz/v1/pdf",
    headers={"Authorization": f"Bearer {os.environ['PDFPIPE_KEY']}"},
    json={
        "html": "<h1>Invoice #4012</h1><p>...</p>",
        "options": {
            "format": "A4",
            "footer_html": FOOTER,
        },
    },
)
resp.raise_for_status()

with open("invoice.pdf", "wb") as f:
    f.write(resp.content)

Styling notes

Headers and footers render in a separate browser frame from the main document. A few things to keep in mind:

  • Inline styles work reliably. External stylesheets and web fonts do not load in the header/footer frame.
  • Set width: 100% on the root element to span the full page width.
  • Use padding: 0 1cm to match the horizontal margin of the main document body.
  • Font size of 9 to 11px is typical. The header/footer area is small; large text will overflow.
  • Background colors are printed in the header/footer frame without needing print_background: true.

Try the header/footer options in the playground (expand “Options” to reveal the header/footer fields), or read the full API reference.

See pricing →