curl --request POST \
--url https://api.parallellabs.app/api/v0/canvas \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"html": "<string>",
"format": "<string>",
"width": 123,
"height": 123,
"animated": true,
"duration": 123
}
'import requests
url = "https://api.parallellabs.app/api/v0/canvas"
payload = {
"companyId": "<string>",
"html": "<string>",
"format": "<string>",
"width": 123,
"height": 123,
"animated": True,
"duration": 123
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
companyId: '<string>',
html: '<string>',
format: '<string>',
width: 123,
height: 123,
animated: true,
duration: 123
})
};
fetch('https://api.parallellabs.app/api/v0/canvas', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.parallellabs.app/api/v0/canvas",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'companyId' => '<string>',
'html' => '<string>',
'format' => '<string>',
'width' => 123,
'height' => 123,
'animated' => true,
'duration' => 123
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.parallellabs.app/api/v0/canvas"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.parallellabs.app/api/v0/canvas")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/canvas")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}"
response = http.request(request)
puts response.read_body{
"imageId": "<string>",
"videoId": "<string>",
"url": "<string>",
"width": 123,
"height": 123,
"duration": 123
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Render a canvas art image or animation from HTML
curl --request POST \
--url https://api.parallellabs.app/api/v0/canvas \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"companyId": "<string>",
"html": "<string>",
"format": "<string>",
"width": 123,
"height": 123,
"animated": true,
"duration": 123
}
'import requests
url = "https://api.parallellabs.app/api/v0/canvas"
payload = {
"companyId": "<string>",
"html": "<string>",
"format": "<string>",
"width": 123,
"height": 123,
"animated": True,
"duration": 123
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
companyId: '<string>',
html: '<string>',
format: '<string>',
width: 123,
height: 123,
animated: true,
duration: 123
})
};
fetch('https://api.parallellabs.app/api/v0/canvas', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.parallellabs.app/api/v0/canvas",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'companyId' => '<string>',
'html' => '<string>',
'format' => '<string>',
'width' => 123,
'height' => 123,
'animated' => true,
'duration' => 123
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.parallellabs.app/api/v0/canvas"
payload := strings.NewReader("{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.parallellabs.app/api/v0/canvas")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.parallellabs.app/api/v0/canvas")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"companyId\": \"<string>\",\n \"html\": \"<string>\",\n \"format\": \"<string>\",\n \"width\": 123,\n \"height\": 123,\n \"animated\": true,\n \"duration\": 123\n}"
response = http.request(request)
puts response.read_body{
"imageId": "<string>",
"videoId": "<string>",
"url": "<string>",
"width": 123,
"height": 123,
"duration": 123
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Company API key - scoped to a specific company. Generate from the Integrations page in your dashboard.
Body
Canvas to render
Company ID whose credits are billed for the call
Complete standalone HTML document for the canvas. Must be fully self-contained (inline ; Google Fonts links, the Tailwind CDN script, and the Lucide icons CDN script are allowed) with html,body{margin:0} and the body sized to EXACTLY the canvas dimensions with overflow:hidden. Static — no animations — unless animated is true, in which case animate with infinite, seamlessly looping CSS keyframes. Follow the design guidelines in the Canvas tag description.
Named canvas format: 'square' (1080x1080 feed), 'portrait' (1080x1350 feed, default), 'story' (1080x1920 Stories/Reels), 'landscape' (1200x630 link preview), 'slide' (1920x1080 slide background), 'wide' (1920x1080), 'tall' (1080x1620), 'pin' (1000x1500), 'linkedin_banner' (1584x396), 'twitter_header' (1500x500), 'facebook_cover' (1640x924), 'youtube_thumb' (1280x720), 'email_header' (1200x400), 'a4_portrait' (1240x1754), 'a4_landscape' (1754x1240), 'letter_portrait' (1275x1650), 'business_card' (1050x600). Ignored when width/height are given.
Custom canvas width in px (200-2160); requires height. Overrides format.
Custom canvas height in px (200-2160); requires width. Overrides format.
Record the canvas as a looping MP4 video instead of a still PNG. The response returns videoId and an MP4 url instead of imageId. Costs 3 credits instead of 0.1. Default false.
Video length in seconds when animated is true (1-15, out-of-range values are clamped; default 5). Match it to the CSS animation cycle so the loop tiles cleanly. Ignored for stills.
Response
The rendered canvas with its public image (or video) URL
Rendered canvas
ID of the saved image (still canvases only)
ID of the saved video (animated canvases only)
Permanent public URL of the rendered PNG, or of the recorded MP4 when animated
Rendered width in px
Rendered height in px
Video length in seconds (animated canvases only)

