ReflowPDF API api.reflowpdf.com/v1

    HTML to PDF

    A whole HTML document, printed by the engine of the editor.

    curl https://api.reflowpdf.com/v1/pdf \
      -H "Authorization: Bearer $REFLOWPDF_API_KEY" \
      -H "Content-Type: application/json" \
      -d @request.json -o report.pdf

    The document#

    • Send the whole document: <!doctype html>, <head> with its <style>, <body>.
    • Images, stylesheets and fonts by https URL or data: URI; @font-face with your fonts is honoured. Relative addresses (logo.png) point where options.base_url says.
    • What the document names and the print did not get is left out and named: skipped_resources in the JSON response, ReflowPDF-Skipped-Resources beside the file. That is an image, stylesheet, font or embedded document that could not be fetched, an address while allow_remote is false, or a relative address with no base_url. A font family it names that no face answers (missing_fonts) took the next family of its list; characters no face has are in missing_glyphs.
    • Scripts do not run unless options.javascript asks for them (JavaScript); otherwise send the HTML as it should print.

    Paper and pages#

    The document's own CSS decides: @page { size: A4; margin: 20mm }, @page :first, named pages, break-before: page, orphans/widows. Running headers and footers are @page margin boxes:

    <style>
      @page {
        size: Letter;
        margin: 25mm 20mm;
        @top-right { content: "Quarterly report"; font: 9pt Georgia; color: #666 }
        @bottom-center { content: "Page " counter(page) " of " counter(pages) }
      }
      h2 { break-before: page }
      table { break-inside: auto } thead { display: table-header-group }
    </style>

    Where the document's CSS names no paper, options.paper gives it: a size (A3, A4, A5, B4, B5, Letter, Legal, Ledger, Tabloid, or two lengths such as "100mm 150mm"), an orientation and a margin. Without either: A4 portrait, 20 mm margins. Both are a user-agent @page rule — what the document's own @page declares wins, property by property: a document that names only size: Letter gets Letter with these margins.

    Headers and footers are @page margin boxes, and a margin box is as tall as its margin: the default 20 mm holds a line or two. For a taller header, such as a logo or two lines of address, set a larger margin-top or margin-bottom. A margin you set is used as written: with margin: 0 a header or footer has no room and is cut off at the edge of the paper.

    Standards and options#

    OptionWhatPlans
    pdf_ua: 1PDF/UA-1 (ISO 14289-1): tagged, accessibleall
    pdf_ua: 2PDF/UA-2 (ISO 14289-2), PDF 2.0Pro, Max
    pdf_a: "3a" / "1b"PDF/A-3a / PDF/A-1b, archivalPro, Max
    pdf_xPDF/X-4 / PDF/X-1a, print-ready for a pressPro, Max

    Every PDF is tagged with its structure, language and title, so screen readers read it in order. What each claim means and which go together: PDF/UA, PDF/A, PDF/X. Headers and footers, watermarks, encryption, attachments, metadata, compression and the rest: Print options.

    The response#

    By default the PDF itself (Content-Type: application/pdf), with X-Page-Count and a Content-Disposition naming its file (your filename, else the document's <title>). With Accept: application/json, its link and facts instead — the file kept 24 hours:

    {
      "id": "Hq8vR2cT9xKmL4pW",
      "object": "pdf",
      "url": "https://api.reflowpdf.com/v1/files/Hq8vR2cT9xKmL4pW/Q3%20report.pdf",
      "expires_at": "2026-11-03T10:22:31.000Z",
      "filename": "Q3 report.pdf",
      "pages": 12,
      "bytes": 418274,
      "mark": null,
      "missing_variables": [],
      "skipped_resources": []
    }

    A page by its address#

    Instead of html, send url: a public page, fetched by us and printed as it is.

    curl https://api.reflowpdf.com/v1/pdf \
      -H "Authorization: Bearer $REFLOWPDF_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url": "https://example.com/", "options": {"paper": {"size": "Letter"}}}' -o page.pdf
    • It is fetched anonymously (no cookie, no login), as a browser's first visit would get it: http(s) on ports 80 and 443, a public address only, redirects followed (up to 5, each checked again), within 20 seconds and 15 MB. Its text is decoded by the charset it declares.
    • Its relative addresses resolve from where it was found (after the redirects), unless options.base_url names another place.
    • Its scripts run with options.javascript: true (JavaScript); without it, a page that builds itself with JavaScript prints as its HTML arrives. A page behind a login — fetch it yourself and send it as html.
    • A page that could not be fetched is 422 document_unreachable, its detail saying why. Up to 5 pages are fetched at once per account, 600 per 10 minutes. url goes with data too: the page is then a template, filled first.

    A template of your own, inline#

    Send data beside html, and the HTML is a template filled first — see the template language.