Documentation Index
Fetch the complete documentation index at: https://docs.handtextai.com/llms.txt
Use this file to discover all available pages before exploring further.
1) Get an API key
Create an API key in the HandTextAI dashboard and copy the value (it starts with htext_).
2) Generate a PNG
For the most predictable production output, start with font_id: 1 or 2 and validate your final settings with /preview before you ship.
curl -sS https://api.handtextai.com/api/v1/generate \
-H "Authorization: Bearer htext_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello from HandTextAI!",
"font_id": 1,
"font_size": "auto",
"page_size": "a4",
"dpi": 300
}' \
| python3 - <<'PY'
import sys, json, base64
payload = json.load(sys.stdin)
img = base64.b64decode(payload["image_base64"])
open("output.png", "wb").write(img)
print("Saved output.png (request_id:", payload.get("request_id"), ")")
PY
import requests
import base64
API_KEY = "htext_YOUR_KEY"
response = requests.post(
"https://api.handtextai.com/api/v1/generate",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"text": "Hello from HandTextAI!",
"font_id": 1,
"font_size": "auto",
"page_size": "a4",
"dpi": 300,
},
)
if response.status_code == 200:
data = response.json()
image_bytes = base64.b64decode(data["image_base64"])
with open("output.png", "wb") as f:
f.write(image_bytes)
print(f"Saved output.png ({data['width']}x{data['height']})")
else:
print(f"Error {response.status_code}: {response.text}")
const API_KEY = "htext_YOUR_KEY";
const response = await fetch("https://api.handtextai.com/api/v1/generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
text: "Hello from HandTextAI!",
font_id: 1,
font_size: "auto",
page_size: "a4",
dpi: 300,
}),
});
if (response.ok) {
const data = await response.json();
const imageBuffer = Buffer.from(data.image_base64, "base64");
require("fs").writeFileSync("output.png", imageBuffer);
console.log(`Saved output.png (${data.width}x${data.height})`);
} else {
console.error(`Error ${response.status}: ${await response.text()}`);
}
3) Preview before you generate (free)
Use /preview to iterate on margins/font sizing quickly. Previews are watermarked and free.
curl -sS https://api.handtextai.com/api/v1/preview \
-H "Authorization: Bearer htext_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello!", "font_id": 1, "font_size": "auto", "page_size": "a4"}'
response = requests.post(
"https://api.handtextai.com/api/v1/preview",
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json={"text": "Hello!", "font_id": 1, "font_size": "auto", "page_size": "a4"},
)
data = response.json()
print(f"Preview: {data['preview_width']}x{data['preview_height']}")
const previewResponse = await fetch("https://api.handtextai.com/api/v1/preview", {
method: "POST",
headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({text: "Hello!", font_id: 1, font_size: "auto", page_size: "a4"}),
});
const preview = await previewResponse.json();
console.log(`Preview: ${preview.preview_width}x${preview.preview_height}`);
4) Generate a PDF
PDF responses are binary, not JSON.
curl -sS https://api.handtextai.com/api/v1/generate \
-H "Authorization: Bearer htext_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello from HandTextAI!",
"font_id": 1,
"font_size": "auto",
"page_size": "letter",
"output_format": "pdf"
}' \
-o output.pdf
response = requests.post(
"https://api.handtextai.com/api/v1/generate",
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json={
"text": "Hello from HandTextAI!",
"font_id": 1,
"font_size": "auto",
"page_size": "letter",
"output_format": "pdf",
},
)
if response.status_code == 200:
with open("output.pdf", "wb") as f:
f.write(response.content)
print("Saved output.pdf")
else:
print(f"Error {response.status_code}: {response.text}")
const pdfResponse = await fetch("https://api.handtextai.com/api/v1/generate", {
method: "POST",
headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({
text: "Hello from HandTextAI!",
font_id: 1,
font_size: "auto",
page_size: "letter",
output_format: "pdf",
}),
});
if (pdfResponse.ok) {
const buffer = await pdfResponse.arrayBuffer();
require("fs").writeFileSync("output.pdf", Buffer.from(buffer));
console.log("Saved output.pdf");
}
5) Handle rate limits
When you hit rate limits, use the Retry-After header:
import time
def generate_with_retry(request_data, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
"https://api.handtextai.com/api/v1/generate",
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json=request_data,
)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited, waiting {retry_after}s...")
time.sleep(retry_after)
continue
return response
raise Exception("Max retries exceeded")
async function generateWithRetry(requestData, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch("https://api.handtextai.com/api/v1/generate", {
method: "POST",
headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify(requestData),
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
console.log(`Rate limited, waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
return response;
}
throw new Error("Max retries exceeded");
}
Need to generate many outputs?
Use the dashboard’s Batch Generation for CSV-based bulk processing.
Troubleshooting
| Error | Cause | Solution |
|---|
401 | Invalid API key | Check Authorization: Bearer htext_... format |
422 | Validation error | Check details array in response for field errors |
429 | Rate/quota limit | Wait for Retry-After seconds, then retry |
See Errors & Rate Limits for complete error reference.