Blog/Guides

Excel to PDF with an xlsx to pdf api

Guides··5 min read

Use POST /v1/convert when the source is an Excel workbook. Send exactly one of html, url, or file. For a .xlsx (or .xls) file, send file as base64 plus sourceFilename with the real extension, and set to to pdf. LibreOffice is the default engine. Chromium on POST /v1/pdf does not open spreadsheets. Product page: https://relaypdf.com/office-to-pdf. Field list: https://relaypdf.com/docs/convert.

What this endpoint is not

POST /v1/pdf prints HTML, a public URL, Markdown, or a published Handlebars template. It is not an Office importer. POST /v1/convert is the LibreOffice and wkhtmltopdf path. Do not merge PDFs here; that is POST /v1/pdf/merge. Do not screenshot a live sheet in a browser here; that is POST /v1/images.

wkhtmltopdf only accepts html or url to pdf, and the only documented convert option is options.toc on that engine. Spreadsheets use engine libreoffice (the default). Sending a workbook to wkhtmltopdf is the wrong engine.

First request

Bearer key. JSON body. file is base64; a data URI prefix is optional. sourceFilename is required with file and must include an extension so LibreOffice picks the filter. The convert docs use this Excel body:

curl https://api.relaypdf.com/v1/convert
  -H "Authorization: Bearer pdf_live_..."
  -H "Content-Type: application/json"
  -d '{"file":"UEsDB...","sourceFilename":"quarterly.xlsx","to":"pdf"}'
  --output quarterly.pdf

to defaults to pdf if you omit it. Documented targets are pdf, docx, xlsx, html, and png (a thumbnail on the product page). filename, if you send it, is the output name. response defaults to binary. Successful bytes carry x-relaypdf-id, x-relaypdf-size, and content-disposition. Set response to url for a 24-hour GET /v1/files/:id link (no key on download). Set response to async for HTTP 202 and GET /v1/jobs/:id. callbackUrl, if present, must be https.

Print area and landscape live in the workbook

ConvertRequest does not document printArea, landscape, pageOrientation, pageSize, scale, or sheet index. OpenAPI lists options.toc only, and that flag is for wkhtmltopdf. options.landscape on POST /v1/pdf is a Chromium print flag. It is not a convert field. Do not copy it onto /v1/convert and expect Calc to honor it.

LibreOffice prints the file you send. Page setup that already exists in the .xlsx — print area, landscape or portrait, paper size, fit-to-page, repeating header rows, print titles, hidden sheets — is part of that file. Set those in Excel or Calc, save, then convert. If the PDF shows unused columns or a portrait crop you did not want, the workbook’s print area or page orientation is wrong. Fix the source and POST again. The API will not invent a sheet picker. Hidden columns and filtered views are also file state; the worker does not apply a filter you did not save.

Wide finance grids usually need landscape and a defined print area in the file. Leave those unset and LibreOffice uses Calc defaults for that document. That is not a RelayPDF default you can override in JSON.

CSV too

There is no separate CSV endpoint. /office-to-pdf lists Word, Excel, PowerPoint, text, images, email files, and other LibreOffice-openable formats. A .csv is that class of source: send file plus sourceFilename ending in .csv, to pdf, default engine. LibreOffice opens the bytes as a Calc document and writes a PDF. There is no documented delimiter, encoding, or locale field on ConvertRequest. Put a UTF-8 CSV in the payload and name it correctly.

{
  "file": "aWQsYW1vdW50LGRhdGU...",
  "sourceFilename": "export.csv",
  "to": "pdf",
  "filename": "export.pdf"
}

A public https URL to a downloadable .xlsx or .csv is the url source, exclusive of file and html. Private, loopback, and metadata hosts are url_not_allowed. html on convert is HTML into LibreOffice (or wkhtml), not a spreadsheet parser.

SDK, CLI, MCP

Python 0.1.2: convert.create, convert.from_path, convert.from_html, convert.wkhtml. from_path reads a local file and sets sourceFilename. JSON field names stay camelCase. Node 0.1.3 matches: convert.fromPath, convert.fromHtml, convert.wkhtml, convert.create. CLI: relaypdf convert quarterly.xlsx --to pdf --out quarterly.pdf. MCP tool: convert. First LibreOffice call after idle can take on the order of a minute (Node README: up to about 90s); use response async if your HTTP client will not wait.

from relaypdf import RelayPDF
import os
client = RelayPDF(api_key=os.environ["RELAYPDF_API_KEY"])
pdf = client.convert.from_path("quarterly.xlsx", to="pdf")
pdf.save("quarterly.pdf")
job = client.convert.from_path(
    "quarterly.xlsx", to="pdf", response="async"
)
done = client.jobs.wait(job.id)
file = client.files.download(done["id"])
file.save("quarterly.pdf")

After the PDF exists

Convert returns one file. Chain documented PDF tools if you need a pack or a lock: POST /v1/pdf/merge (2–20 FileSource items), POST /v1/pdf/protect, POST /v1/pdf/stamp, POST /v1/pdf/extract. Tools debit separately from convert. Raster (POST /v1/pdf/raster) is for page previews of the PDF you already have, not for opening .xlsx.

Errors and cost

codeTypical HTTPMeaning
invalid_request400Wrong exclusive source, missing sourceFilename, bad to/engine
url_not_allowed400Private url, or callbackUrl is not https
payload_too_large413Reduce the base64 file; no published MB cap
convert_unavailable503Document worker cold or unreachable; retry; not billed
processing_failed502Worker failed the job; not billed
rate_limited429Honor Retry-After; not billed
payment_required402Empty wallet; not billed

Failed operations, 429s, and 402s are never billed. LibreOffice convert is $0.04; wkhtmltopdf is $0.025; Chromium PDF and screenshots are $0.015; tools are $0.005 (SDK READMEs / wallet). Trial is $5.00 (5000 millicents). Trial rate 20/min; funded or auto-reload 60/min; burst 5 per 10 seconds. Query-string keys are rejected. GET /v1/account is not billed.

Do not invent these fields

Do not send worksheetActive, worksheetIndex, AutoFit, FitToPage, ClearPrintArea, Scale, PageOrientation, or PDF/A flags. Those names belong to other vendors. RelayPDF convert documents html | url | file, sourceFilename, filename, to, engine, response, callbackUrl, and options.toc. That is the list.

If you need Chromium print CSS, headers, or waitUntil, build the table as HTML and use POST /v1/pdf. If you already have a designed workbook and want a faithful print, keep POST /v1/convert and put print area and landscape in the .xlsx.

Ship it

Start on https://relaypdf.com/office-to-pdf. Word uses the same ConvertRequest; see https://relaypdf.com/blog/docx-to-pdf-api. OpenAPI: https://relaypdf.com/openapi.json. One bearer key covers convert, Chromium PDF, screenshots, and tools.

Ready to generate?

One API for HTML, Markdown, URLs, and Office. REST, SDK, CLI, or MCP.