This guide shows how to use PDFCrowd's HTML to PDF API from Rust with reqwest. Your program sends an authenticated HTTP request containing a webpage URL, an HTML file, or an HTML string. PDFCrowd performs the conversion and returns the PDF in the response body.
Set up the example
To run the example, you need Rust and Cargo. Download Cargo.toml and main.rs. Put them in a project directory with this layout:
pdfcrowd-example/
├── Cargo.toml
└── src/
└── main.rs
The manifest enables reqwest's blocking client and multipart form support:
[dependencies] reqwest = { version = "0.13", features = ["blocking", "multipart"] }
Set your API credentials in the API_USERNAME
and API_KEY environment variables. Both can be demo for testing. For example, in a POSIX shell:
export API_USERNAME=demo export API_KEY=demo
Convert a webpage to PDF
From the project directory, build and run the downloaded program with a URL and an output filename:
cargo run -- url https://example.com/ output.pdf
The request goes to https://api.pdfcrowd.com/convert/24.04/ over HTTPS, using your API username and API key
for HTTP Basic authentication. The example sends multipart form fields; reqwest supplies the
request's content type and boundary.
The following excerpt shows the request construction. In the complete program, username and
api_key come from the environment, and client is a reqwest::blocking::Client configured
with connection and request timeouts:
use reqwest::blocking::multipart::Form; let form = Form::new() .text("input_format", "html") .text("output_format", "pdf") .text("content_viewport_width", "balanced") .text("page_size", "A4") .text("url", "https://example.com/"); let response = client.post("https://api.pdfcrowd.com/convert/24.04/") .basic_auth(username, Some(api_key)) .multipart(form) .send()?;
input_format and output_format select HTML to PDF conversion. The url field specifies the
page to convert; it is separate from the API endpoint. content_viewport_width=balanced lets
the converter choose a viewport width for the page. After checking for HTTP status 200, the
program writes the binary response to output.pdf.
The webpage and its resources must be reachable by PDFCrowd. A URL on localhost refers to
the conversion server, not the computer running this program.
Convert an HTML file or string
Use one input field per request: url, file, or text. The authentication and response
handling stay the same.
Upload an HTML file
For a local file such as report.html, replace the .text("url", ...) call with:
.file("file", "report.html")?;
Form::file() opens the file and adds it as an upload. The downloadable program exposes this as:
cargo run -- file report.html report.pdf
If the document depends on local images or stylesheets, package them with the HTML in an archive
and upload the archive as file. Uploading the HTML alone does not include neighboring files.
See the archive input example for supported formats
and choosing the main HTML file.
Send an HTML string
For HTML produced by your application, replace the .text("url", ...) call with:
.text("text", "<h1>Monthly report</h1><p>Revenue increased.</p>");
You can also try a short string with the downloaded program:
cargo run -- text '<h1>Monthly report</h1><p>Revenue increased.</p>' report.pdf
Rust strings are UTF-8. Images and stylesheets in uploaded or string HTML can use absolute,
publicly reachable URLs. To resolve relative URLs against a public location, include a
<base href="https://example.com/assets/"> element in the HTML document's <head>.
Customize the PDF
Conversion options are additional form fields. For example, extend the form before passing it
to .multipart() to set the top and bottom margins and use print stylesheets:
let form = form .text("margin_top", "20mm") .text("margin_bottom", "20mm") .text("use_print_media", "true");
The main example already sets page_size to A4; replace that value to change the page format.
Use the HTTP parameter names and string values from the
parameter reference. Add each option once to the form.
Handle errors
A completed HTTP transfer can still contain an API error. The program checks the HTTP status before opening the output file:
let status = response.status(); if status != reqwest::StatusCode::OK { let details = response.text()?; return Err(std::io::Error::other( format!("PDFCrowd returned HTTP {status}: {details}") ).into()); } let pdf = response.bytes()?; std::fs::write(output, &pdf)?;
The PDF is read as bytes. Error bodies are read as text so the reported failure includes the API's explanation. The HTTP response documentation explains API errors.
The complete program propagates setup, request, and file-writing errors with ?; main() prints
the error and exits with a nonzero status. It uses a 30-second connection timeout and a 120-second
total request timeout; adjust these for your application's expected conversion time.
This example buffers the response in memory. For large outputs, a streaming implementation can write to a temporary file and keep it only after the transfer and HTTP status checks succeed.
Use reqwest in an async application
The example uses blocking I/O for a small command-line program. In an async application, use
reqwest::Client and reqwest::multipart::Form, awaiting the request and response reads.
The async Form::file()
also needs .await and the stream feature. The endpoint, authentication, and form fields are
the same. Avoid calling the blocking client inside an async runtime; see
reqwest's blocking client documentation.