For developers
A brief in, a finished thumbnail out
Describe the video, say the words, and get back a YouTube thumbnail with the headline painted in. No canvas to build, no fonts to pick, no text to place. Sign in, create a key in the console, and call it from your own product.
curl -X POST https://app.hitcanvas.com/v1/thumbnails \
-H "authorization: Bearer $HITCANVAS_KEY" \
-H "content-type: application/json" \
-d '{
"topic": "a producer watching a song go viral overnight",
"headline": "1M streams in 7 days",
"style": "bold"
}'The answer, in about a minute:
{
"id": "0c1f…", // the library item it was kept as
"picture": "/v1/library/0c1f…/image", // fetch it again with the same key
"image": "data:image/png;base64,…", // 1280 × 720, the headline painted in
"headline": "1M streams in 7 days",
"model": "…",
"requestId": "req_…",
"usage": { "generations": { "used": 12, "cap": 200 }, "resetsAt": "…" }
}The brief
Everything POST /v1/thumbnails takes is about the picture, never about the canvas.
topic— what the video is about. The one required field.headline,tagline— the words. Leave the headline out and HitCanvas writes one from the topic (counts as an idea).style— the picture’s look;lettering— the words’ finish, kind of face and slant, if you care.role,subject,featured— who is in it: the story’s subject, or a host at the edge with the story behind them. A saved person bypersonaId(with one of their looks aslookId), whose consent was recorded when they were saved; or photos sent along (one to three of the same face, withpermission: true); and how they look.expression,intensity,pose— the face and the body, in words.kitId— a brand kit fromGET /v1/kits. Wherever the brief is silent the kit speaks: style, lettering, colours, tagline, role. Its person becomes the subject when none is named — or sendsubject: { anyone: true }to have the model draw someone instead — and its logo is set in its corner of the finished picture.nichefromPOST /v1/study,conceptfromPOST /v1/direct— the picture steered by what wins in the niche and by a chosen idea. Neither is required.x-hitcanvas-spaceheader — which client it is for.Idempotency-Keyheader — so a retry never pays twice. OrPOST /v1/jobswithkind: "thumbnails"to get a 202 now and poll for the picture.
Endpoints
Contract 2.9.2 · OpenAPI 3.1 — every field, enum and status code, built from the same schemas the routes parse with. Generate a client from it.
Thumbnails
The public API: a brief in, a finished thumbnail out. Each counts against the workspace's monthly allowance.
- POST /v1/thumbnails
- A finished thumbnail from a creative brief: the words painted in, nothing to set or render. One generation.generate
- POST /v1/improve
- The same thumbnail back with a review's changes made. One generation.generate
Ideas
What to make: the niche studied, concepts, headlines, and a review of a finished thumbnail. Studies and ideas count against their own allowances.
- POST /v1/study
- The niche's outperforming thumbnails, and a brief on what they share. Cached for a day and not counted when served from cache.generate
- POST /v1/direct
- Three concepts for the picture: emotion, pose and background. Counts as an idea.generate
- POST /v1/headlines
- Five headlines from what the video is about. Counts as an idea.generate
- POST /v1/review
- A finished thumbnail checked against its niche brief. Counts as an idea.generate
Jobs
The same work, answered with a 202 and finished after.
- POST /v1/jobs
- Any of the picture calls as a job: 202 now, the route's own response later.generate
- GET /v1/jobs/{id}
- A job's status and, once finished, its result or error.generate
Library
Every thumbnail the workspace has made, kept as it was made.
- GET /v1/library
- Everything this space has made, newest first.library:read
- DELETE /v1/library
- Clear the pictures that were never saved as a design (?unsaved=1).library:write
- GET /v1/library/{id}
- One item with its text layers and the settings behind it.library:read
- DELETE /v1/library/{id}
- Remove an item.library:write
- GET /v1/library/{id}/image
- The picture itself. ?which=preview for the rendered preview; ?download=1 to get it as an attachment.library:read
Brand kits
- GET /v1/kits
- The space's brand kits.kits:read
- POST /v1/kits
- Create a brand kit.kits:write
- PUT /v1/kits/{id}
- Replace a kit's name, look and logo.kits:write
- PATCH /v1/kits/{id}
- Move a kit to another client space.Studio · owner or admin
- DELETE /v1/kits/{id}
- Remove a kit. Refused while personas belong to it.Studio · owner or admin
- GET /v1/kits/{id}/logo
- The kit's logo image.kits:read
Personas
Saved people. Photos need recorded consent.
- GET /v1/personas
- The space's saved people.personas:read
- POST /v1/personas
- Save a person: one to three photos of the same face, with recorded consent, filed under a brand.personas:write
- PATCH /v1/personas/{id}
- Move a persona to another brand.personas:write
- DELETE /v1/personas/{id}
- Remove a person and their photos.personas:write
- GET /v1/personas/{id}/photo
- One of the person's photos: ?n=0..2, or ?look=<lookId>&n=….personas:read
- POST /v1/personas/{id}/photo
- Add a photo at an angle the person does not have yet. Three at most.personas:write
- DELETE /v1/personas/{id}/photo
- Remove photo ?n=…. Two must remain.personas:write
- POST /v1/personas/{id}/looks
- Save a look: a named description of how the person is styled, with its own photos.personas:write
- DELETE /v1/personas/{id}/looks
- Remove look ?look=<lookId>.personas:write
Spaces
Client spaces: an agency's clients, each with its own library, kits and personas.
- GET /v1/spaces
- The client spaces you can work in, which is active, and (owners and admins) who is in each.
- POST /v1/spaces
- Create a client space.Studio · owner or admin
- PATCH /v1/spaces
- Change who is in which space.Studio · owner or admin
- POST /v1/spaces/active
- Move your session into a space. Sets a cookie; every request checks it again.Studio session
- PUT /v1/spaces/{id}
- Rename a space.Studio · owner or admin
- DELETE /v1/spaces/{id}
- Remove a space, moving its work to ?moveTo=general or another space's id.Studio · owner or admin
- PUT /v1/invitations/{id}/spaces
- Which client spaces an invited person may work in.Studio · owner or admin
- POST /v1/invitations/{id}/spaces
- Claim the spaces an invitation granted, on accepting it.Studio session
Videos
Published videos watched against their channel's typical video: public data, read by HitCanvas with its own YouTube credentials. No YouTube account of yours is involved.
- GET /v1/videos
- Published videos being watched, each against its channel's typical video. Public data, read by HitCanvas with its own YouTube credentials.videos:read
- POST /v1/videos
- Watch a video by link.videos:write
- DELETE /v1/videos/{id}
- Stop watching a video.videos:write
Workspace
- GET /v1/usage
- This month against the workspace's allowance, and when it resets. Owners and admins, signed in, also get it by key and by space.
- GET /v1/keys
- The workspace's API keys, each with the scopes it holds and the client spaces it may work in.Studio session
- POST /v1/keys
- Make a key. The key itself is in the answer once.Studio · owner or admin
- PATCH /v1/keys/{id}
- Narrow or widen a key: its scopes, its client spaces, its name. Takes effect on its next call.Studio · owner or admin
- DELETE /v1/keys/{id}
- Revoke a key. Anything using it stops working at once.Studio · owner or admin
- POST /v1/billing/checkout
- Start a subscription. The workspace's owner only.Studio session
- POST /v1/billing/portal
- Open the billing portal. The workspace's owner only.Studio session
AdvancedStudio-facing · compatibility
Studio-facing: the engine directly, layered designs, fonts, server rendering. Deprecated for new integrations, served for existing ones under the compatibility path.
- POST /v1/generate
- The engine directly: layout mode (a background with measurements for words you set) or full mode (what POST /v1/thumbnails wraps).generatedeprecated
- POST /v1/library
- Save a layered design onto the item it was made from, or as a new one.library:writedeprecated
- POST /v1/renders
- A layered design drawn as a file: 3840 × 2160 (or the 1280 × 720 frame) as JPG or PNG.library:readdeprecated
- GET /v1/fonts
- Every headline face a layered design can name, with the exact fontFamily and fontWeight to put in a text layer.deprecated
- GET /v1/youtube
- YouTube channels a signed-in person connected in the Studio. Sessions only; a key is refused.Studio sessiondeprecated
- GET /v1/youtube/{id}/uploads
- A Studio-connected channel's latest uploads, unlisted and private included. Sessions only; a key is refused.Studio sessiondeprecated
- DELETE /v1/youtube/{id}
- Disconnect a channel connected in the Studio.Studio · owner or admindeprecated
Meta
- GET /v1/health
- Whether the API is up, and which contract it speaks.
- GET /v1/openapi.json
- This document.
Routes marked Studio need a signed-in browser session and are not reachable with an API key. The scope beside a route is what a key needs to call it.
What is Studio-only
HitCanvas Studio has a second way of working: AI-plus-text, where the model paints a background composed around a headline the Studio then sets in real type, on its own canvas, with its own fonts, layers, arrows, logos and export. That is a product of the Studio’s typography and canvas, and it is not the API’s. An integration should not have to reproduce a canvas, choose fonts, read clearance measurements, manage layered designs or render a file to get a thumbnail — so the public API does not ask it to. Studio-only: AI-plus-text creation, layered editing, canvas controls, font choice, and the Studio’s render and export.
Compatibility and deprecation
- The operations that serve the Studio’s way of working over HTTP —
POST /v1/generatein layout mode,POST /v1/library(saving a layered design),POST /v1/renders,GET /v1/fonts— keep working for integrations that built on them. They are grouped under Advanced above, markeddeprecatedin the contract withx-hitcanvas-audience: studio, and every answer from them carriesDeprecation: trueand aLink … rel="deprecation"header pointing here. They are not part of new public guidance, and new examples use onlyPOST /v1/thumbnails. - Nothing on that surface is restricted or removed silently. Before any change in access: notice to every workspace that has called it in the previous ninety days, a contract version that says what changes and when, and this page updated. A breaking change gets a new path version;
/v1keeps answering as documented until then. - The one exception so far: contract 2.7.0 made the
/v1/youtubeoperations session-only and retired theyoutube:readscope. Those operations expose a person’s own Studio connection, which an API customer never has, so a key lost nothing it could use. POST /v1/generatein full mode is the same enginePOST /v1/thumbnailswraps; it stays, and answers with the engine’s full record. New integrations should take the plainer brief.
Keys and limits
- Keys belong to your workspace, not to a person, so they keep working when someone leaves.
- A key is shown once and stored hashed. Revoke it in the console at any time.
- Send it as a bearer token. Keys are limited to 60 calls a minute.
- A key holds scopes —
generate,library:read,library:write,kits:read,kits:write,personas:read,personas:write,videos:read,videos:write— and may be limited to certain client spaces. Set both when you make a key in the console or withPOST /v1/keys; change them withPATCH /v1/keys/<id>;GET /v1/keysshows them. A call the key may not make is a 403key_scope_deniedorkey_space_denied. A key made before scopes existed is unrestricted and stays so until you narrow it. - API calls count against the same monthly allowance as the studio, and a call that fails is not counted.
- Agencies: send an
x-hitcanvas-spaceheader with a client space’s id to work in that client’s library and brand kits.GET /v1/spaceslists them, and marks oneisDefault: that is where a call works when you send no header, an empty one, orgeneral. A header naming anything else — a malformed id, another workspace’s space, or one that has since been removed — is a 404 and nothing is written. It is never swapped silently for the default space. - Reference photos in a request are passed to the image model and not stored. Photos are kept only when someone saves a person with
POST /v1/personas, which needs an explicit consent field and thekitIdof the brand the persona belongs to. A brand holds up to three. - YouTube: you never connect a YouTube account to use the API. Your HitCanvas key is the only credential; every piece of public YouTube data that research and generation need (
POST /v1/study,GET /v1/videos) HitCanvas fetches itself, with its own YouTube Data API credentials. Google OAuth exists only inside the Studio, where a signed-in person may connect their own channel for things only their account can do — picking one of their existing thumbnails to review, say. That connection is not part of the public API and is never required for making thumbnails; the/v1/youtubeoperations that expose it are session-only, and a key calling them is refused withsession_required. - Every response carries an
x-request-idheader; include it if you write to support about a specific call. - Send an
Idempotency-Keyheader (any string up to 255 characters, unique to the attempt) on any POST, PUT or PATCH, and the same request again returns the same answer without doing the work or charging again, markedIdempotency-Replayed: true. The same key with a different body is a 422idempotency_key_reused; the same key while the first is still running is a 409idempotency_in_progress. A 5xx is never kept, so a failed generation can be retried with its key. Keys are yours for 24 hours. - Or do not wait at all:
POST /v1/jobsanswers 202 with an id and does the work after answering, andGET /v1/jobs/<id>tells you when it is done and hands you the result. A job still running when its route’s time limit passes is marked failed withjob_timed_out, and nothing is charged for work that did not finish. Jobs take anIdempotency-Keylike any other POST. - Every error, 4xx or 5xx, is one shape:
{ error: { code, message, type, param?, requestId } }.codeis stable and specific (invalid_request,not_found,usage_limit_exceeded);typeis the class to branch on without knowing every code (validation,authentication,authorization,not_found,conflict,rate_limit,provider,unavailable,internal);paramnames the field when one is at fault;requestIdmatches thex-request-idheader. A 429usage_limit_exceededmeans your workspace used its monthly allowance; a 503service_capacity_exceededmeans HitCanvas itself is out of room and was never yours to fix by upgrading. - The API path is versioned (
/v1) and additive changes within it are tracked by the contract version above; a breaking change gets a new path version and a deprecation notice first, never a silent removal.