Font Generation API
Generate custom fonts from a text prompt or a style reference image. Version 3.
Base URL
https://api.lipi.ai/v3
Authentication
All requests require an API key passed in the x-api-key header.
curl https://api.lipi.ai/v3/credits \ -H "x-api-key: lpi_your_api_key_here"
Never expose your API key in client-side code or public repositories. Use environment variables.
Endpoints
/v3/font-generate/from-textGenerate a font from a text prompt10 credits/v3/font-generate/from-imageGenerate a font from a style image10 or 30 credits/v3/font-generate/:idPoll job status and download URLsPOST /v3/font-generate/from-text
Generate a font from a prose description. The job runs asynchronously. Poll the returned job_id for the finished font files. Typical processing time is 2 to 5 minutes.
Request fields
| Field | Required | Description |
|---|---|---|
| prompt | yes | Font description, 1 to 500 characters |
| font_name | no | Display name for the font (defaults to a generated name) |
| seed | no | Integer 0 to 4294967295 for reproducible results |
Request
curl -X POST https://api.lipi.ai/v3/font-generate/from-text \
-H "x-api-key: lpi_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "gothic blackletter with ornate capitals",
"font_name": "MyFont",
"seed": 42
}'Response (201)
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"mode": "text",
"credits_charged": 10,
"credits_remaining": 90,
"free_credits_remaining": 0,
"poll_url": "/v3/font-generate/550e8400-e29b-41d4-a716-446655440000"
}POST /v3/font-generate/from-image
Generate a font from a style or handwriting reference image. Set character_set to"standard" (90 glyphs, 10 credits) or "extended" (260 glyphs, 30 credits).
Images larger than 1536px on either dimension are automatically resized (aspect ratio preserved). Maximum request body is 6MB, which is roughly a 4MB raw image. Resize client-side for larger files.
Request fields
| Field | Required | Description |
|---|---|---|
| style_image | yes | Base64 data URL (data:image/png;base64,...). PNG, JPEG, or WebP |
| font_name | no | Display name for the font (defaults to a generated name) |
| character_set | no | "standard" (default, 10 credits) or "extended" (30 credits) |
Request
curl -X POST https://api.lipi.ai/v3/font-generate/from-image \
-H "x-api-key: lpi_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"style_image": "data:image/png;base64,iVBORw0KGgo...",
"font_name": "MyHandwriting",
"character_set": "standard"
}'Response (201)
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"mode": "image",
"character_set": "standard",
"credits_charged": 10,
"credits_remaining": 90,
"free_credits_remaining": 0,
"poll_url": "/v3/font-generate/550e8400-e29b-41d4-a716-446655440000"
}GET /v3/font-generate/:id
Poll for job completion. Poll every 3 to 5 seconds. Download URLs are valid for 24 hours and are re-issued fresh on every poll, so a job can be re-polled at any time to get working links.
Response (running)
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"mode": "text",
"progress": 42,
"stage": "sd_running",
"font_name": "MyFont",
"created_at": "2026-05-15T19:00:00Z",
"completed_at": null
}Response (succeeded)
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "succeeded",
"mode": "text",
"progress": 100,
"stage": "completed",
"font_name": "MyFont",
"created_at": "2026-05-15T19:00:00Z",
"completed_at": "2026-05-15T19:03:30Z",
"download_urls": {
"otf": "https://fontbuster-job-outputs-prod.s3.amazonaws.com/...",
"ttf": "https://fontbuster-job-outputs-prod.s3.amazonaws.com/...",
"woff": "https://fontbuster-job-outputs-prod.s3.amazonaws.com/..."
},
"download_urls_expire_at": "2026-05-16T19:03:30Z"
}OTF, TTF, and WOFF are delivered. WOFF2 is not currently included.
Response (failed)
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"mode": "text",
"progress": 0,
"stage": "failed",
"font_name": "MyFont",
"created_at": "2026-05-15T19:00:00Z",
"completed_at": "2026-05-15T19:01:10Z",
"error": "Font generation failed: Modal endpoint returned 503",
"credits_refunded": 10
}Credits are automatically refunded when a job fails because of an infrastructure error.
Error Codes
| Status | Error Code | Description |
|---|---|---|
| 400 | missing_prompt | Empty or missing "prompt" (from-text) |
| 400 | missing_image | Missing "style_image" (from-image) |
| 401 | invalid_api_key | Missing or invalid API key |
| 402 | insufficient_credits | Not enough credits for this generation |
| 403 | key_suspended | API key has been suspended |
| 404 | not_found | Job not found or not owned by this key |
| 413 | payload_too_large | Request body exceeds the 6MB limit |
| 422 | prompt_too_long | Prompt exceeds 500 characters |
| 422 | invalid_seed | Seed is not an integer in range |
| 422 | invalid_image | Not a valid PNG, JPEG, or WebP image |
| 422 | invalid_character_set | character_set is not "standard" or "extended" |
| 429 | rate_limited | Exceeded rate limit (see retry_after_seconds) |
| 503 | service_busy | High demand, retry shortly |
Rate Limits
Per Key
60 req/min
Response Header
429 responses include retry_after_seconds
Pricing
| Generation type | Credits |
|---|---|
| Text to font | 10 |
| Image to font, standard (90 glyphs) | 10 |
| Image to font, extended (260 glyphs) | 30 |
Credits are charged when the job is submitted. A failed job is automatically refunded. A successful generation is not refundable.
Code Examples
Python
import requests
import time
API_KEY = "lpi_your_api_key_here"
BASE = "https://api.lipi.ai/v3"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}
# Submit a text-to-font job
resp = requests.post(f"{BASE}/font-generate/from-text", json={
"prompt": "gothic blackletter with ornate capitals",
"font_name": "MyFont",
"seed": 42
}, headers=HEADERS)
job = resp.json()
print(f"Job {job['job_id']} created. Credits remaining: {job['credits_remaining']}")
# Poll for completion
while True:
time.sleep(4)
status = requests.get(f"{BASE}/font-generate/{job['job_id']}", headers=HEADERS).json()
if status["status"] == "succeeded":
for fmt, url in status["download_urls"].items():
data = requests.get(url).content
with open(f"MyFont.{fmt}", "wb") as out:
out.write(data)
print(f"Saved MyFont.{fmt}")
break
elif status["status"] == "failed":
print(f"Failed: {status.get('error')}")
breakNode.js
const fs = require('fs');
const API_KEY = 'lpi_your_api_key_here';
const BASE = 'https://api.lipi.ai/v3';
const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };
async function generateFont(prompt) {
const res = await fetch(`${BASE}/font-generate/from-text`, {
method: 'POST',
headers,
body: JSON.stringify({ prompt, font_name: 'MyFont' })
});
const job = await res.json();
console.log(`Job ${job.job_id} created`);
while (true) {
await new Promise(r => setTimeout(r, 4000));
const status = await fetch(`${BASE}/font-generate/${job.job_id}`, { headers }).then(r => r.json());
if (status.status === 'succeeded') {
for (const [fmt, url] of Object.entries(status.download_urls)) {
const buf = Buffer.from(await fetch(url).then(r => r.arrayBuffer()));
fs.writeFileSync(`MyFont.${fmt}`, buf);
console.log(`Saved MyFont.${fmt}`);
}
return;
}
if (status.status === 'failed') throw new Error(status.error);
}
}
generateFont('gothic blackletter with ornate capitals');