Rust PDF Generation SDK
The official cloudlayerio crate provides async-first PDF and image generation for Rust. Convert HTML to PDF, capture URLs as documents, and render templates — with zero unsafe code and minimal dependencies.
Installation
[dependencies]
cloudlayerio = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
Requirements: Rust 1.75+, tokio 1.x
Setup
Get your API key by creating a free account at cloudlayer.io. Your account comes with free API credits for testing.
use cloudlayerio::{CloudLayerClient, ApiVersion};
let client = CloudLayerClient::new("YOUR-API-KEY", ApiVersion::V2)?;
The SDK requires you to choose an API version:
| v1 | v2 | |
|---|---|---|
| Default mode | Synchronous (returns binary) | Asynchronous (returns Job) |
| Sync response | Raw binary (PDF/image bytes) | JSON Job object |
| Binary access | Direct from ConversionResult::Binary | Via wait_for_job() + download_job_result() |
URL to PDF
v2 (recommended)
v2 returns a Job object. Use wait_for_job() and download_job_result() to get the binary:
use cloudlayerio::types::response::ConversionResult;
use cloudlayerio::types::{UrlToPdfOptions, UrlOptions, WaitForJobOptions};
let result = client.url_to_pdf(UrlToPdfOptions {
url_opts: UrlOptions {
url: Some("https://example.com".to_string()),
..Default::default()
},
..Default::default()
}).await?;
if let ConversionResult::Job { job, .. } = result {
let completed = client.wait_for_job(&job.id, WaitForJobOptions::default()).await?;
let pdf = client.download_job_result(&completed).await?;
std::fs::write("output.pdf", &pdf)?;
}
v1
v1 returns the binary directly, no polling needed:
use cloudlayerio::{CloudLayerClient, ApiVersion};
use cloudlayerio::types::response::ConversionResult;
let client = CloudLayerClient::new("YOUR-API-KEY", ApiVersion::V1)?;
let result = client.url_to_pdf(opts).await?;
if let ConversionResult::Binary { data, .. } = result {
std::fs::write("output.pdf", &data)?;
}
URL to Image
use cloudlayerio::types::{UrlToImageOptions, UrlOptions};
let result = client.url_to_image(UrlToImageOptions {
url_opts: UrlOptions {
url: Some("https://example.com".to_string()),
..Default::default()
},
..Default::default()
}).await?;
HTML to PDF
HTML must be base64-encoded before sending. Use the built-in encode_html() helper:
use cloudlayerio::{encode_html};
use cloudlayerio::types::{HtmlToPdfOptions, HtmlOptions, BaseOptions, PdfOptions};
let html = r#"<h1>Hello, World!</h1><p>This is my PDF.</p>"#;
let result = client.html_to_pdf(HtmlToPdfOptions {
base: BaseOptions::default(),
html_opts: HtmlOptions { html: encode_html(html) },
pdf_opts: PdfOptions::default(),
}).await?;
HTML to Image
use cloudlayerio::types::{HtmlToImageOptions, HtmlOptions, BaseOptions, ImageOptions};
let result = client.html_to_image(HtmlToImageOptions {
base: BaseOptions::default(),
html_opts: HtmlOptions { html: encode_html(html) },
image_opts: ImageOptions::default(),
}).await?;
Template to PDF
use cloudlayerio::types::{TemplateToPdfOptions, TemplateOptions};
let result = client.template_to_pdf(TemplateToPdfOptions {
template_opts: TemplateOptions {
template_id: Some("YOUR-TEMPLATE-ID".to_string()),
data: Some(serde_json::json!({ "name": "Alice", "total": "$99.00" })),
..Default::default()
},
..Default::default()
}).await?;
Template to Image
use cloudlayerio::types::{TemplateToImageOptions, TemplateOptions};
let result = client.template_to_image(TemplateToImageOptions {
template_opts: TemplateOptions {
template_id: Some("YOUR-TEMPLATE-ID".to_string()),
..Default::default()
},
..Default::default()
}).await?;
Document Conversion
DOCX to PDF
use std::path::Path;
use cloudlayerio::types::BaseOptions;
let result = client.docx_to_pdf(Path::new("input.docx"), BaseOptions::default()).await?;
DOCX to HTML
let result = client.docx_to_html(Path::new("input.docx"), BaseOptions::default()).await?;
PDF to DOCX
let result = client.pdf_to_docx(Path::new("input.pdf"), BaseOptions::default()).await?;
Merge PDFs
use cloudlayerio::types::{MergePdfsOptions, UrlOptions, Batch};
let result = client.merge_pdfs(MergePdfsOptions {
url_opts: UrlOptions {
batch: Some(Batch {
urls: vec![
"https://example.com/a.pdf".to_string(),
"https://example.com/b.pdf".to_string(),
],
}),
..Default::default()
},
..Default::default()
}).await?;
Data Management
Jobs & Assets
// List all jobs
let jobs = client.list_jobs().await?;
// Get a specific job
let job = client.get_job("JOB-ID").await?;
// List all assets
let assets = client.list_assets().await?;
// Get a specific asset
let asset = client.get_asset("ASSET-ID").await?;
Account
let account = client.get_account().await?;
println!("Calls used: {:?}/{:?}", account.calls, account.calls_limit);
Templates
use cloudlayerio::types::ListTemplatesParams;
// List all templates
let templates = client.list_templates(None).await?;
// Filter by type
let params = ListTemplatesParams {
template_type: Some("pdf".to_string()),
..Default::default()
};
let pdf_templates = client.list_templates(Some(params)).await?;
// Get a specific template
let template = client.get_template("TEMPLATE-ID").await?;
Storage
use cloudlayerio::types::response::StorageParams;
// List storage configurations
let items = client.list_storage().await?;
// Add a new S3 storage configuration
let params = StorageParams {
title: "My Bucket".to_string(),
region: "us-east-1".to_string(),
access_key_id: "YOUR-ACCESS-KEY".to_string(),
secret_access_key: "YOUR-SECRET".to_string(),
bucket: "my-bucket".to_string(),
endpoint: None,
};
let response = client.add_storage(params).await?;
// Delete storage
client.delete_storage("STORAGE-ID").await?;
Configuration
Full control via ClientConfig:
use std::collections::HashMap;
use cloudlayerio::{CloudLayerClient, ClientConfig, ApiVersion};
let config = ClientConfig {
api_key: "YOUR-API-KEY".to_string(),
api_version: ApiVersion::V2,
base_url: "https://api.cloudlayer.io".to_string(),
timeout_secs: 30,
max_retries: 2,
user_agent: "my-app/1.0".to_string(),
additional_headers: HashMap::new(),
};
let client = CloudLayerClient::with_config(config)?;
| Field | Type | Default | Description |
|---|---|---|---|
api_key | String | — | Your cloudlayer.io API key |
api_version | ApiVersion | — | V1 or V2 |
base_url | String | https://api.cloudlayer.io | API base URL |
timeout_secs | u64 | 30 | Request timeout in seconds |
max_retries | u8 | 2 | Retries for 429/5xx (0–5) |
user_agent | String | SDK version string | User-Agent header |
Error Handling
use cloudlayerio::CloudLayerError;
match client.url_to_pdf(opts).await {
Ok(result) => { /* handle result */ }
Err(CloudLayerError::Auth(e)) => eprintln!("Auth error {}: {}", e.status_code, e.message),
Err(CloudLayerError::RateLimit(e)) => {
eprintln!("Rate limited. Retry after: {:?}s", e.retry_after);
}
Err(CloudLayerError::Api(e)) => eprintln!("API error {}: {}", e.status_code, e.message),
Err(CloudLayerError::Timeout(e)) => eprintln!("Timeout: {}", e.message),
Err(CloudLayerError::Network(e)) => eprintln!("Network: {}", e.message),
Err(CloudLayerError::Validation(e)) => eprintln!("Validation: {}", e.message),
Err(e) => eprintln!("Error: {e}"),
}
Blocking / Sync API
For synchronous code that can’t use an async runtime, enable the blocking feature:
[dependencies]
cloudlayerio = { version = "0.1", features = ["blocking"] }
use cloudlayerio::{CloudLayerBlockingClient, ApiVersion};
use cloudlayerio::types::{UrlToPdfOptions, UrlOptions};
use cloudlayerio::types::response::ConversionResult;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = CloudLayerBlockingClient::new("YOUR-API-KEY", ApiVersion::V1)?;
let result = client.url_to_pdf(UrlToPdfOptions {
url_opts: UrlOptions {
url: Some("https://example.com".to_string()),
..Default::default()
},
..Default::default()
})?;
if let ConversionResult::Binary { data, .. } = result {
std::fs::write("output.pdf", &data)?;
}
Ok(())
}
⚠️ Runtime Warning:
CloudLayerBlockingClientpanics if called from within a tokio runtime. Only use this client from synchronous context:fn main(), non-async tests, orstd::thread::spawn.