Polotno Studio API
Quickstart
The Polotno Studio API renders images and videos from your templates. It is designed for no-code automation (Zapier, Make, n8n) and direct API calls. This guide gets you from an API key to your first rendered image. For the full endpoint-by-endpoint reference, see the API Reference.
1. Get an API key
API keys are per project. Open the editor, open the projects side-panel, and enable automation on a project — the key is shown once, so copy it immediately. The key scopes every request to that project (its templates, renders, and webhooks).
Send it on every request as a Bearer token, or in the X-API-Key header:
# Either header works:
curl https://api.studio.polotno.com/v1/templates \
-H "Authorization: Bearer key_live_xxx"
curl https://api.studio.polotno.com/v1/templates \
-H "X-API-Key: key_live_xxx"Live keys are prefixed key_live_; test keys key_test_. A revoked or invalid key returns 401; a project with automation disabled returns 409.
2. Render your first image
POST /v1/images creates a render. By default it is asynchronous and returns 202 with a pending image. Add ?sync=true to wait up to 30 seconds for a terminal result (201 if the render completes or fails within the window, otherwise 202 with a pending render to poll).
curl -X POST https://api.studio.polotno.com/v1/images \
-H "Authorization: Bearer key_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"template_id": "tpl_xxx",
"dynamic_fields": [
{ "name": "headline", "text": "50% OFF Black Friday" },
{ "name": "product_image", "image_url": "https://example.com/shoe.jpg" }
],
"format": "png",
"pixel_ratio": 1,
"webhook_url": "https://hooks.zapier.com/...",
"metadata": { "campaign_id": "camp_2026" }
}'A dynamic_fields entry targets one element by its name and sets one or more of: text, image_url, video_url, color, font_family, font_size, visible. format is png, jpeg, or pdf; pixel_ratio must be greater than 0 and at most 10.
3. Map fields in Zapier / Make / n8n
To show friendly field names in a no-code tool, call GET /v1/templates/{id}/dynamic-fields. It returns a flat list; each field's key encodes the element name and the property to set (e.g. fields__headline__text or fields__product_image__image_url), alongside a human label, a type (string / url / integer / color / boolean), and a required flag:
{
"fields": [
{
"key": "fields__headline__text",
"label": "Headline",
"type": "string",
"required": false,
"help_text": "Main title at the top",
"default": "Default headline"
},
{
"key": "fields__product_image__image_url",
"label": "Product Image (URL)",
"type": "url",
"required": false
}
]
}4. Get the result
Poll GET /v1/images/{id} until status is completed and read image_url — or subscribe a webhook (below) and receive the finished render with no polling.
curl https://api.studio.polotno.com/v1/images/img_xxx \
-H "Authorization: Bearer key_live_xxx"5. Subscribe to webhooks (optional)
Instead of polling, create a webhook with POST /v1/webhooks. The response includes a one-time secret you use to verify the HMAC signature on each delivery. Events include image.completed, image.failed, video.completed, video.failed, and bulk-job events. Manage subscriptions and inspect deliveries under /v1/webhooks — see the reference.
6. Rate limits, idempotency & errors
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; a 429 adds Retry-After. Make POSTs safe to retry by sending an Idempotency-Key header — a replay returns the original response with Idempotency-Replayed: true.
Errors share one envelope:
{
"error": {
"type": "validation_error",
"code": "invalid_request",
"message": "template_id is required",
"param": "template_id",
"request_id": "req_xxx"
}
}