1) Get an API key
Create an API key in the HandTextAI dashboard and copy the value (it starts withhtext_).
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
- Python
- JavaScript
Copy
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
Copy
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}")
Copy
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
- Python
- JavaScript
Copy
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"}'
Copy
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']}")
Copy
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
- Python
- JavaScript
Copy
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
Copy
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}")
Copy
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 theRetry-After header:
- Python
- JavaScript
Copy
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")
Copy
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 |