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.

crates.io docs.rs GitHub

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:

v1v2
Default modeSynchronous (returns binary)Asynchronous (returns Job)
Sync responseRaw binary (PDF/image bytes)JSON Job object
Binary accessDirect from ConversionResult::BinaryVia wait_for_job() + download_job_result()

URL to PDF

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)?;
FieldTypeDefaultDescription
api_keyStringYour cloudlayer.io API key
api_versionApiVersionV1 or V2
base_urlStringhttps://api.cloudlayer.ioAPI base URL
timeout_secsu6430Request timeout in seconds
max_retriesu82Retries for 429/5xx (0–5)
user_agentStringSDK version stringUser-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: CloudLayerBlockingClient panics if called from within a tokio runtime. Only use this client from synchronous context: fn main(), non-async tests, or std::thread::spawn.