PDF generation API: keys, requests, and responses
The thirds.ai PDF generation API turns your HTML, or a saved template with your data, into a finished PDF or image. You create an API key, send a request from your server with Authorization: Bearer YOUR_API_KEY, and download the file when it's ready. Each finished file costs one credit, and a file that fails costs nothing. A verified free account gets 50 credits a month, and you don't need a card. When you need more, compare plan and credit pack prices.
Give this prompt to your AI agent. The agent does the task for you.
Read https://thirds.ai/docs/api and help me set up API key authentication and handle thirds.ai responses in my app without sharing my key in chat.
With thirds.ai, you save a design once, such as an invoice, a report, a certificate, or a social post. Then you send new details and get back a file with your logo, colours, and fonts. Your code doesn't lay out each file or run its own browser to print pages.
You don't need code to make files. How to make a template you can reuse shows how to make one in the editor and download it. For a complete working example in code, generate your first PDF with the API.
Authentication
The thirds.ai API checks every request for an API key, which you send in the Authorization: Bearer YOUR_API_KEY header. You get a key in three steps, and you need a thirds.ai account with a verified email address. Keep the key on your server and out of browser code.
- Sign in with an email link, GitHub, or Google. If your account has no email address, open Settings, choose Add email under Sign-in methods, and request a verification link. Open that link in the same browser while you stay signed in, then click Complete sign in. Your free monthly credits become available after you verify the address.
- Open API keys and create a named key.
- Copy the key before you leave the page and put it in your server's secret store. You can only see the full key once. Later reads show only its prefix.
The key gives access to its account. Keep it out of prompts, URLs, logs, and source control. Use a separate key for each integration. If you lose a key, revoke it and update each client that uses it. An account can hold ten active API keys. A signed download link needs no API key, since its own token carries the authority to read that one file.
Use the studio to manage your account, sign in, buy credits, and edit designs. It uses a secure session cookie, and browser writes also need the session's x-csrf-token. Your app uses the API endpoints that accept bearer keys. Don't use browser session or checkout routes to authenticate an integration.
Send a request
A request to make a PDF is one HTTPS POST to https://thirds.ai/v1/pdf with a JSON body. You send HTML or a saved template with its data, and you need your API key and one available credit. The API doesn't make a file from a page URL.
Send JSON with Content-Type: application/json. For an asset upload, use the media type listed for that endpoint. The API rejects JSON fields it doesn't know, so check the PDF render fields before you add one. This request makes a PDF from a short piece of HTML.
curl --fail-with-body --silent --show-error https://thirds.ai/v1/pdf \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: YOUR_UNIQUE_PDF_REQUEST' \
--data '{"html":"<h1>Invoice 1042</h1><p>Total due: $480.00</p>","wait":false}'const response = await fetch("https://thirds.ai/v1/pdf", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
"Idempotency-Key": "YOUR_UNIQUE_PDF_REQUEST"
},
body: JSON.stringify({
"html": "<h1>Invoice 1042</h1><p>Total due: $480.00</p>",
"wait": false
}),
redirect: "error",
signal: AbortSignal.timeout(30000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}; Retry-After: ${response.headers.get("retry-after") ?? "none"}`);
console.log(await response.text());import json
from urllib.error import HTTPError
from urllib.request import HTTPRedirectHandler, Request, build_opener
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
request = Request("https://thirds.ai/v1/pdf",
method="POST",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
"Idempotency-Key": "YOUR_UNIQUE_PDF_REQUEST"
},
data=json.dumps({
"html": "<h1>Invoice 1042</h1><p>Total due: $480.00</p>",
"wait": False
}).encode("utf-8"),
)
try:
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(response.read().decode("utf-8"))
except HTTPError as error:
retry = error.headers.get("Retry-After", "none")
raise RuntimeError(f"HTTP {error.code}; Retry-After: {retry}") from NoneChoose your own Idempotency-Key for each file, and reuse that key if you retry the same request. To make an image, send the same kind of request to POST /v1/image with the image render fields. The API reference lists each endpoint's fields, headers, and response types. You can also download the OpenAPI file.
Responses and errors
A create request returns a JSON object with the job's ID and status, and you download the output file in a separate request after the render succeeds. The status tells you whether the file is ready, so read it before you use any link. The API reference documents each status and response code.
200from a create request means the render finished, failed, or was cancelled during the request's wait period.202means thirds.ai accepted and saved the job. Keep its ID and poll for the result, or let a webhook receiver hear when it finishes.200from a status read means the read worked. It doesn't mean the render succeeded.
Keep the x-request-id from each response if you need help from support. This ID is safe to share. HTTP errors contain an error object with a fixed code, a message, field details, and retry guidance when it applies. A failed render has its own error.category and error.code. Read how to retry requests and download files before you repeat a write request. If you get a 429 response, API rate limits shows how long to wait and how many jobs one key can run at once.
We check the request examples against the OpenAPI schemas. Use shared types and security for exact header rules and shared error responses.