Skip to content

SDKs

You can call the Pagr API directly over HTTP, but the official SDKs make it easier: they wrap the base URL, attach your API key, parse responses into typed objects, turn HTTP errors into exceptions you can catch, retry transient read failures, and validate list filters client-side. They cover the whole API — templates and versions, every rendering shape, validation, document browsing, fonts, org stats, service probes, and webhook signature verification and payload parsing.

Language Package Client Sync / async Notes
cURL (raw HTTP) Call the API directly with any HTTP client — no library to install. See Quickstart.
Python pagr-python (GitHub) PagrApiClient Async only Built on httpx, full type hints. The reference implementation — most actively maintained; the others are kept at feature parity with it.
TypeScript pagr (npm) PagrApiClient Async only (native fetch) Zero runtime dependencies, Node.js 18+. Classes parse API responses into iterable result types.
Java final Maven coordinates TBD PagrApiClient / PagrAsyncApiClient Both — a sync client plus an async client returning CompletableFuture Gradle library on a Java 21 toolchain. Gson is an api dependency: JsonObject/JsonElement are part of the public surface, so it lands on your compile classpath deliberately.
C# / .NET Pagr.Sdk (NuGet) PagrApiClient Async only net8.0, sealed strongly-typed models, Async suffix, CancellationToken throughout, built on System.Text.Json.
Ruby pagr (RubyGems, unpublished) Pagr::Client Synchronous only Ruby 3.0+, built on Faraday, idiomatic Ruby naming (see below).
C++ pagr_sdk (CMake package) pagr::PagrApiClient Both — every call has a sync form and an _async form C++20, integrated via CMake, RAII for resource management.

The Java SDK’s package is still on a placeholder identifier internally — don’t build against a Maven coordinate for it yet; ask us for the current artifact when you request access.

Construct a client with your API key, render one document, and save the PDF:

Terminal window
pip install git+https://github.com/Metanous-BV/pagr-python.git
import asyncio
from pagr import PagrApiClient
async def main():
async with PagrApiClient("pagr_test_xxxxxxxxxxxxxxxx") as client:
result = await client.render(
"8bec66ff-6f3d-4c1e-9a2b-1f0e5d7c4a90",
json_data={"Title": "Acme Q3 Invoice", "Amount": 42},
include_document=True,
)
if result.ok:
result.document.save("out/")
asyncio.run(main())

The base URL defaults to the hosted Pagr API — only the key is required. Everything is async; use await and async with.

Whichever language you use, the SDKs share the same behaviour — so what you learn in one carries over:

  • Same authentication — a bearer API key passed at construction, swappable at runtime with set_api_key. See Authentication.
  • Same error split — protocol failures raise; business outcomes (validation failure, insufficient credit, partial batch) come back as data on the result object.
  • Same error mapping — one small exception tree, with transport failures (timeouts, connection errors) folded into it, so you only ever catch one family. See Handle errors and retries.
  • Same retry policy — idempotent GETs retried on 500/502/503/504, timeouts and connection errors, with capped exponential backoff and full jitter. Writes never; 429 never.
  • Same rendering model — single, synchronous batch and async-job renders, with the same counts, status values and issues on every result. See Choose a rendering shape.
  • Same pagingskip/take with a total and a client-side “more pages?” flag. See Listing & pagination.
  • Same client-side filter validation — an unknown filter field or operator raises immediately instead of silently returning the unfiltered result set.
  • Same webhook verification and parsing — one function verifies an incoming callback’s X-Pagr-Signature against the raw body and then turns it into the right typed progress or completion object, validating the shape. Verification fails by raising, never by returning a boolean you could drop.

The SDKs are at feature parity: every endpoint, every rendering shape and every safety behaviour is in all six. The two rows below that still differ are deliberate design decisions rather than gaps waiting to be filled.

Capability Python TypeScript Java C# Ruby C++
Every endpoint
Retry policy on reads
Webhook payload parsing
Webhook signature verification
Raw-PDF render (render_pdf)
Per-call timeout override
Typed list/batch overload for validate
Client-side filter validation
Filename sanitisation on save()
Cancel a wait_for_job poll
Cancel an in-flight request
Sync and async client

The two rows that differ are deliberate, not backlog:

  • Cancelling an in-flight HTTP request needs a transport-level abort hook that Ruby’s net_http adapter and C++’s HTTP client don’t offer. Both still let you cancel a wait_for_job poll (Ruby’s cancelled: predicate, C++’s std::stop_token), which is where waiting actually happens. See Configure the client.
  • A separate sync and async client is a per-language design choice in Java alone, where the async surface (PagrAsyncApiClient) is a distinct type rather than an option on one client. C++ and the other four expose a single client — C++’s PagrApiClient just offers both a sync method and an _async counterpart on the same object. See Configure the client.

Method names follow each language’s conventions rather than Python’s, so the same call reads natively everywhere:

Operation Python TypeScript Java C# Ruby C++
Render render render render RenderAsync render render
Render a raw PDF render_pdf renderPdf renderPdf RenderPdfAsync render_pdf render_pdf
Render a batch render_batch renderBatch renderBatch RenderBatchAsync render_batch render_batch
Enqueue async enqueue_batch_render enqueueBatchRender enqueueBatchRender EnqueueBatchRenderAsync enqueue_batch_render enqueue_batch_render
Poll a job get_job_status getJobStatus getJobStatus GetJobStatusAsync job_status get_job_status
Validate validate validate validate ValidateAsync validate validate
List templates get_templates getTemplates getTemplates GetTemplatesAsync templates get_templates
List documents get_documents getDocuments getDocuments GetDocumentsAsync documents get_documents
Download a PDF download_document downloadDocument downloadDocument DownloadDocumentAsync download_document download_document
Org stats get_org_stats getOrgStats getOrgStats GetOrgStatsAsync org_stats get_org_stats
Verify and parse a callback parse_signed_callback parseSignedCallback Webhooks.parseSignedCallback WebhookSignature.ParseSignedCallback Pagr.parse_signed_callback pagr::webhooks::parse_signed_callback
Verify a signature only verify_signature verifySignature Webhooks.verifySignature WebhookSignature.Verify Pagr.verify_signature pagr::webhooks::verify_signature
Parse a callback (already verified) parse_callback parseCallback RenderCallback.parse RenderCallback.Parse Pagr.parse_callback pagr::webhooks::parse_callback

The verify-and-parse form is the one to reach for: it takes the raw request body, decodes the JSON itself, and so can’t be called in the wrong order or skipped. Note C# spells the verify-only helper Verify rather than VerifySignature — the class it lives on is already WebhookSignature.

Note Ruby drops the get_ prefix throughout (client.templates, not client.get_templates) — an accessor doesn’t announce itself as a getter in Ruby.