Developer docs
Calligraphy Generator API documentation
Use API keys to generate transparent PNG calligraphy images for Arabic names, cursive names, tattoo previews, wedding stationery, and product personalization.
Authentication
Send your API key in either header. Dashboard endpoints use your Supabase access token; public render endpoints use `cg_live_...` API keys.
Authorization: Bearer cg_live_your_key
# or
x-api-key: cg_live_your_keyPOST /api/v1/generate
Required: `text` (120 characters max), `style`. Optional: `resolution`, `transparent_background`, `text_color`, `background_color` (6-digit hex), `response_format` (`data_url` or `base64`).
snake_case names are canonical; camelCase aliases (`textColor`, `backgroundColor`, `transparentBackground`, `responseFormat`, and `fontStyle` for `style`) are also accepted. If both forms are sent, the snake_case value wins.
curl https://www.calligraphy-generator.com/api/v1/generate \
-H "Authorization: Bearer cg_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"text": "Layla",
"style": "Diwani",
"resolution": "SQUARE_HD",
"transparent_background": true,
"text_color": "#111827",
"response_format": "data_url"
}'const response = await fetch('https://www.calligraphy-generator.com/api/v1/generate', {
method: 'POST',
headers: {
Authorization: 'Bearer cg_live_your_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
text: 'Layla',
style: 'Diwani',
resolution: 'SQUARE_HD',
transparent_background: true,
response_format: 'data_url',
}),
});
const generation = await response.json();
console.log(generation.image.data_url);import requests
response = requests.post(
'https://www.calligraphy-generator.com/api/v1/generate',
headers={'Authorization': 'Bearer cg_live_your_key'},
json={
'text': 'Layla',
'style': 'Diwani',
'resolution': 'SQUARE_HD',
'transparent_background': True,
'response_format': 'data_url',
},
)
response.raise_for_status()
print(response.json()['image']['data_url'])Response headers
Every successful generate response includes usage and rate-limit headers; 429 responses add `Retry-After`.
| Header | Description |
|---|---|
| X-Request-Id | Unique request ID — include it in support requests. |
| X-Usage-Limit | Monthly render quota for your plan. |
| X-Usage-Remaining | Renders remaining in the current billing period. |
| X-RateLimit-Limit | Requests allowed per minute. |
| X-RateLimit-Remaining | Requests remaining in the current one-minute window. |
| Retry-After | On 429 responses only: seconds to wait before retrying. |
GET /api/v1/styles
Returns supported styles grouped by script (Arabic, English, Chinese) with `id`, `name`, `script`, `description`, and `preview_text`, plus output resolutions. Current styles: Diwani, Thuluth, Handwritten, Koufi, signature, calligraphy, bubble, Hanyi, Gen, Yrdzst. Resolutions: HD, SQUARE_HD, SQUARE_4K.
curl https://www.calligraphy-generator.com/api/v1/stylesTry every style live in the API playground.
Webhooks
Pro and Business plans can register up to 3 HTTPS endpoints (from the developer dashboard) to get quota alerts. Events: `quota.threshold_80` fires when usage crosses 80% of the monthly quota; `quota.exhausted` fires when the quota is fully used. Deliveries are one attempt per event with a 5-second timeout — no retries.
POST <your endpoint>
X-Webhook-Event: quota.threshold_80
X-Webhook-Signature: 8f3b2c... (hex SHA-256 HMAC of the raw body)
{
"event": "quota.threshold_80",
"data": { "used": 4000, "quota": 5000 },
"created_at": "2026-06-10T12:00:00.000Z"
}const crypto = require('crypto');
const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET).update(rawBody).digest('hex');
if (expected !== req.headers['x-webhook-signature']) throw new Error('Invalid webhook signature');Plans and limits
Free Test Mode
$0 · 100 renders/month · 20/minute
Starter
$19/mo · 1,000 renders/month · 60/minute
Pro
$49/mo · 5,000 renders/month · 180/minute
Business
$149/mo · 25,000 renders/month · 600/minute
Errors
Errors include a stable code and request ID. Keep the request ID for support. Only successful renders count toward your quota.
{
"error": {
"type": "quota_error",
"code": "quota_exceeded",
"message": "Monthly quota exceeded."
},
"request_id": "req_..."
}| Status | Code | Description |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 400 | invalid_request | A parameter failed validation (missing text, unknown style, bad hex color, ...). |
| 401 | missing_api_key | No API key was sent. |
| 401 | invalid_api_key | The key is unknown or revoked. |
| 413 | request_too_large | Body exceeds 16 KB. |
| 429 | quota_exceeded | Monthly quota used up. Retry-After indicates when the period resets. |
| 429 | rate_limit_exceeded | Per-minute rate limit hit. Retry after Retry-After seconds. |
| 500 | generation_failed | Rendering failed. Failed requests are not billed. |
| 503 | metering_unavailable | Usage metering is temporarily unavailable; the request was not billed. Retry shortly. |