Skip to content
Prolifik
Home › Docs › REST API

Prolifik REST API

Clip videos, produce branded posts, generate AI media and schedule posts to social accounts from your own code. This page covers every endpoint: what it takes, what it returns, and what can go wrong.

Browse documentation

The Prolifik REST API works on the same account as the dashboard, with the same brands, plan limits and ownership checks. It covers the AI Clipper, content jobs and publishing, AI Studio, AI influencers and cloning. If you work inside an AI assistant rather than your own code, the Prolifik MCP server offers these operations as tools.

Updated 13 October 2026

SettingValue
Base URLhttps://app.getprolifik.com/api/v1
FormatJSON in and out. Send Content-Type: application/json with every body.
AuthenticationAn API key as a bearer token: Authorization: Bearer plk_live_...
Who can use itAccounts on a paid plan: Lite, Starter, Pro or Enterprise. AI Studio, influencers and cloning need Starter or higher.
Rate limit120 requests per minute per key, with tighter limits on calls that start paid work
Endpoints61, all documented below
OpenAPI documentapp.getprolifik.com/api/v1/openapi.json (OpenAPI 3.1)

Your first request

  1. In the Prolifik dashboard, open Settings > Developer API and create a key with the scopes you need. Copy it: it is shown once.
  2. Keep it in an environment variable, never in frontend code or a public repository: export PROLIFIK_API_KEY=plk_live_...
  3. List your brands:
curl https://app.getprolifik.com/api/v1/brands \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

A 200 with a brands array means the key works (this call needs the content:read scope). From there, a typical integration does one of three things: clips a long video (create a clip job, poll it, then post a clip), produces branded posts (create a content job, poll it, then publish a piece), or reads results into another tool.

Authentication

Every request carries an API key in the Authorization header:

Authorization: Bearer plk_live_...

Scopes

A key can call only the endpoints its scopes allow, and anything else returns 403 insufficient_scope. Write scopes do not include read scopes, so a key that creates jobs and then reads them needs both. Give each integration the fewest scopes it needs, and add publish:write only to keys that should post publicly.

ScopeWhat it allowsEndpoints
clipper:readRead clip jobs, clips, clip minutes, clip publish records and the accounts a clip can post to.GET /clipper/jobs, GET /clipper/minutes, GET /clipper/jobs/{jobId}, GET /clipper/jobs/{jobId}/status, GET /clipper/publish-destinations, GET /clipper/publishes
clipper:writeCreate and cancel clip jobs, get source upload links, post clips and retry failed clip posts. Uses clip minutes. Posting a clip needs only this scope.POST /clipper/jobs, DELETE /clipper/jobs/{jobId}, POST /clipper/upload-url, POST /clipper/clips/{clipId}/publish, POST /clipper/publishes/{publishId}/retry
content:readRead brands, content sources, content jobs and pieces, the media library, usage, publish destinations and content posts.GET /brands, GET /brands/{brandId}, GET /content/sources, GET /content/sources/{sourceId}, GET /content/jobs, GET /content/jobs/{jobId}, GET /content/pieces, GET /content/pieces/{pieceId}, GET /publish-destinations, GET /content/publishes, GET /content/publishes/{publishId}, GET /usage, GET /content/media
content:writeCreate and edit brands and sources, queue content jobs, edit, retry, regenerate and delete pieces, get demo upload links. Uses renders.POST /brands, PATCH /brands/{brandId}, POST /content/sources, PATCH /content/sources/{sourceId}, DELETE /content/sources/{sourceId}, POST /content/sources/{sourceId}/reanalyze, POST /content/jobs, DELETE /content/jobs/{jobId}, PATCH /content/pieces/{pieceId}, DELETE /content/pieces/{pieceId}, POST /content/pieces/{pieceId}/regenerate, POST /content/pieces/{pieceId}/retry, POST /content/uploads
studio:readRead AI Studio models, credits and generations, AI influencers and cloning jobs, and quote a clone.GET /studio/models, GET /studio/credits, GET /studio/generations, GET /studio/generations/{generationId}, GET /influencers, GET /influencers/{influencerId}, POST /cloning/estimate, GET /cloning/jobs, GET /cloning/jobs/{jobId}
studio:writeQueue AI Studio generations, create and change influencers, clone posts and manage cloning jobs. Spends AI credits.POST /studio/uploads, POST /studio/generations, POST /influencers, DELETE /influencers/{influencerId}, POST /influencers/{influencerId}/regenerate, POST /cloning/jobs, DELETE /cloning/jobs/{jobId}, POST /cloning/jobs/{jobId}/variations, POST /cloning/jobs/{jobId}/localize, POST /cloning/jobs/{jobId}/retry, POST /cloning/pieces/{pieceId}/library
publish:writePost or schedule content pieces, edit or cancel queued content posts, and spread cloned variations across accounts. Creates public posts.POST /content/pieces/{pieceId}/publish, PATCH /content/publishes/{publishId}, DELETE /content/publishes/{publishId}, POST /cloning/jobs/{jobId}/distribute

Requests and responses

Errors

Every error has the same shape: a machine-readable code, a message you can show a person, and the requestId that is also in the x-request-id header.

{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key requires the publish:write scope.",
    "requestId": "0d5c3b8e-6f1a-4c2d-9b7e-3a5f8c1d2e4b"
  }
}

A 402 can add fields: upgradeUrl on subscription_required, and needed, balance and upgradeUrl on insufficient_credits.

{
  "error": {
    "code": "insufficient_credits",
    "message": "This request needs 68 AI credits and the account has 40.",
    "requestId": "7c1e9a3b-2d4f-4e6a-8b1c-5d7f9a2c4e6b",
    "needed": 68,
    "balance": 40,
    "upgradeUrl": "https://app.getprolifik.com/pricing"
  }
}
StatusCodeWhen
400invalid_requestA parameter or body field is missing, has the wrong type or is out of range, or the body is not a JSON object. The message names the field.
401unauthorizedNo key, a key that does not start with plk_live_, or a key that is revoked or expired.
402subscription_requiredThe account has no paid plan, or the plan does not include the feature (AI Studio, influencers and cloning need Starter or higher). upgradeUrl says where to fix it.
402insufficient_creditsThe request costs more AI credits than the account has. needed and balance give the numbers.
402quota_exceededA limit of the plan was reached: clip minutes, the monthly render budget, monthly videos, the brand count, or a brand or account the plan has locked. A subscription with an unpaid invoice also gets this code.
403insufficient_scopeThe key works but lacks the scope this endpoint needs. The WWW-Authenticate header names the scope.
403forbiddenThe account may not use this part of the product right now, for example Clipper calls from an account that has never had a plan or trial.
404not_foundNothing with that ID exists on your account.
409conflictThe resource is in the wrong state (cancelling a finished job, editing a post that already went out), or the Idempotency-Key was used with different data or is still being processed. The message says which.
409in_progressA distribute for this cloning job is already running.
413payload_too_largeThe JSON body is over 64 KB, or an uploaded clip source is over 8 GB.
429rate_limit_exceededToo many requests. Wait the number of seconds in the Retry-After header.
500internal_errorSomething failed on Prolifik's side. Before you retry a call that starts work or posts, list that resource to check whether the first attempt got through.
502upstream_errorThe social posting provider refused to change or cancel a queued post. Nothing changed on Prolifik's side.
503service_unavailableA background worker could not be reached, so the work was not queued and any credits were refunded. Retry shortly.

Branch on code, not on the message text, which can change.

Rate limits

Limits use fixed windows. Over a limit, the request fails with 429 rate_limit_exceeded and a Retry-After header in seconds. The x-ratelimit-* headers describe the 120-per-minute window, even when a tighter per-operation limit answered, so wait on Retry-After.

CallsLimitCounted
Every request120 per minutePer API key
POST /clipper/jobs10 per minute and 30 per hourMinute window per key, hour window shared by every key on the account
POST /brands5 per minutePer key
POST /content/sources and re-analyze10 per minute, sharedPer key
POST /content/jobs5 per minutePer key
POST /content/uploads20 per minutePer key
POST /studio/generations5 per minutePer key
POST /studio/uploads20 per minutePer key
Influencer create, regenerate and delete5 per minute, sharedPer key
Cloning create, add variations, translate, retry and distribute10 per 10 minutes, eachPer key

Pagination

Lists return newest first and take two query parameters:

Every list answers with hasMore and nextCursor. While hasMore is true, ask again with before set to nextCursor exactly as you got it. On the last page hasMore is false and nextCursor is null. Items are ordered by creation time and then by ID, so paging this way never skips or repeats an item, even when a whole batch was created in the same instant.

curl "https://app.getprolifik.com/api/v1/content/pieces?limit=100&before=MjAyNi0xMC0xM1QxMDoxNTowMC4xMDQ1NjdafDFlM2E1YzdiLTlkMmYtNGI2ZS04YTFjLTNlNWY3YjlkMmE0Yw" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

A cursor is opaque. Pass it back unchanged and don't parse or build one: what it holds can change. before also still takes an ISO 8601 timestamp and then returns items created strictly before it. That works, but createdAt is rounded to the millisecond, so a timestamp taken from it can skip items created in the same millisecond as the last one you saw. A before that is neither a cursor nor a timestamp returns 400.

A few lists are short enough to come back whole: brands, influencers, publish destinations, and clip publish records (the latest 200).

Idempotency

Calls that start paid work or post publicly take an Idempotency-Key header, so a retry after a timeout cannot queue a second job, charge twice or post twice.

Long-running work

Clipping, rendering and generation take from seconds to several minutes, so the create call returns straight away (usually with 202) and you poll the resource until it settles. The API has no webhooks today. Polling every 10 to 30 seconds is plenty, and each poll counts toward the 120 requests a minute.

WorkStarted byPollDone when
Clip jobPOST /clipper/jobsGET /clipper/jobs/{jobId}/statuscompleted, failed or cancelled
Brand analysisPOST /brandsGET /brands/{brandId}enrichmentStatus is complete or failed
Content sourcePOST /content/sourcesGET /content/sources/{sourceId}ready or failed
Content jobPOST /content/jobsGET /content/jobs/{jobId}ready_for_review, completed, partially_failed or canceled
Piece edit or retryPATCH or retry a pieceGET /content/pieces/{pieceId}ready or failed
AI Studio generationPOST /studio/generationsGET /studio/generations/{generationId}complete or failed
AI influencerPOST /influencersGET /influencers/{influencerId}base_ready or failed
Cloning jobPOST /cloning/jobsGET /cloning/jobs/{jobId}ready or failed

All endpoints

61 endpoints in ten areas. Every path below is relative to https://app.getprolifik.com/api/v1. In the examples, $PROLIFIK_API_KEY holds your key, and responses are trimmed where they are long: ... marks fields left out.

EndpointWhat it doesScope
GET /clipper/jobsList clip jobsclipper:read
POST /clipper/jobsCreate a clip jobclipper:write
GET /clipper/minutesGet the clip-minute balanceclipper:read
GET /clipper/jobs/{jobId}Get a clip job and its clipsclipper:read
DELETE /clipper/jobs/{jobId}Cancel a clip jobclipper:write
GET /clipper/jobs/{jobId}/statusPoll clip job statusclipper:read
POST /clipper/upload-urlCreate a source upload URLclipper:write
GET /clipper/publish-destinationsList clip publish destinationsclipper:read
POST /clipper/clips/{clipId}/publishPublish or schedule a clipclipper:write
GET /clipper/publishesList clip publish recordsclipper:read
POST /clipper/publishes/{publishId}/retryRetry a failed clip postclipper:write
GET /brandsList brandscontent:read
POST /brandsCreate a brandcontent:write
GET /brands/{brandId}Get a brandcontent:read
PATCH /brands/{brandId}Update a brandcontent:write
GET /content/sourcesList content sourcescontent:read
POST /content/sourcesAdd a content sourcecontent:write
GET /content/sources/{sourceId}Get a content sourcecontent:read
PATCH /content/sources/{sourceId}Rename a source or edit its notescontent:write
DELETE /content/sources/{sourceId}Delete a content sourcecontent:write
POST /content/sources/{sourceId}/reanalyzeRe-analyze a content sourcecontent:write
GET /content/jobsList content jobscontent:read
POST /content/jobsCreate a content jobcontent:write
GET /content/jobs/{jobId}Get a content jobcontent:read
DELETE /content/jobs/{jobId}Cancel a content jobcontent:write
GET /content/piecesList piecescontent:read
GET /content/pieces/{pieceId}Get a piececontent:read
PATCH /content/pieces/{pieceId}Edit a piece and render it againcontent:write
DELETE /content/pieces/{pieceId}Delete a piececontent:write
POST /content/pieces/{pieceId}/regenerateRegenerate a failed piececontent:write
POST /content/pieces/{pieceId}/retryRetry a failed rendercontent:write
GET /publish-destinationsList publish destinationscontent:read
POST /content/pieces/{pieceId}/publishPublish or schedule a piecepublish:write
GET /content/publishesList content postscontent:read
GET /content/publishes/{publishId}Get a content postcontent:read
PATCH /content/publishes/{publishId}Edit a queued post's captionpublish:write
DELETE /content/publishes/{publishId}Cancel a queued postpublish:write
GET /usageGet plan usagecontent:read
GET /content/mediaList the media librarycontent:read
POST /content/uploadsCreate a demo upload URLcontent:write
GET /studio/modelsList AI Studio models and pricesstudio:read
GET /studio/creditsGet the AI credit balancestudio:read
POST /studio/uploadsCreate a reference image upload URLstudio:write
GET /studio/generationsList AI Studio generationsstudio:read
POST /studio/generationsQueue an AI Studio generationstudio:write
GET /studio/generations/{generationId}Get a generationstudio:read
GET /influencersList AI influencersstudio:read
POST /influencersCreate an AI influencerstudio:write
GET /influencers/{influencerId}Get an AI influencerstudio:read
DELETE /influencers/{influencerId}Delete an AI influencerstudio:write
POST /influencers/{influencerId}/regenerateRegenerate an influencer portraitstudio:write
POST /cloning/estimateQuote a clonestudio:read
GET /cloning/jobsList cloning jobsstudio:read
POST /cloning/jobsClone a TikTok or Instagram poststudio:write
GET /cloning/jobs/{jobId}Get a cloning job and its variationsstudio:read
DELETE /cloning/jobs/{jobId}Delete a cloning jobstudio:write
POST /cloning/jobs/{jobId}/variationsAdd variations to a jobstudio:write
POST /cloning/jobs/{jobId}/localizeTranslate variationsstudio:write
POST /cloning/jobs/{jobId}/retryRetry a failed cloning jobstudio:write
POST /cloning/jobs/{jobId}/distributeSchedule variations across accountspublish:write
POST /cloning/pieces/{pieceId}/librarySave a variation to the Librarystudio:write

Clipper

Turn a long YouTube, Twitch or Kick video, or your own upload, into vertical clips with captions, then post them. Clip jobs use clip minutes: 60 a month on Lite, 360 on Starter, 1,400 on Pro, unmetered on Enterprise.

List clip jobs

GET/clipper/jobs

  • Scope clipper:read
  • Returns 200

Your clip jobs, newest first. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/clipper/jobs?limit=20" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "jobs": [
    {
      "job_id": "3f9a1c2b7d4e",
      "status": "completed",
      "source_title": "How we got our first 1,000 users",
      "source_channel": "Acme Podcast",
      "source_url": "https://www.youtube.com/watch?v=aBcDeFgHiJk",
      "num_clips": 3,
      "created_at": "2026-10-13T09:12:44.512Z",
      "completed_at": "2026-10-13T09:21:03.118Z",
      "error": null,
      "thumbs": ["https://...", "https://...", "https://..."],
      "progress": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

In this list, num_clips is the number of clips the job has produced so far. progress is null once a job is completed, failed or cancelled, and otherwise holds step, total_steps, current_step_name and percent. For an uploaded file, source_url is "uploaded".

Create a clip job

POST/clipper/jobs

  • Scope clipper:write
  • Idempotency-Key optional
  • Returns 202

Queues a video for clipping and returns at once. Send one of url, urls or sourceStoragePath. The job uses clip minutes for the length of the source video, so check the balance first: a job needs at least 5 minutes left.

Accepted links: one YouTube video (watch, youtu.be, Shorts, live or embed links), a Twitch VOD or clip, or a Kick VOD or clip. Channel pages, playlists and live streams on Twitch and Kick are refused with 400.

Settings are forgiving. Numbers out of range are clamped, an inverted minDuration/maxDuration pair is swapped, and an unknown value for category, layout, captionStyle or aspectRatio falls back to its default. Unknown or malformed keys inside options are dropped.

Body

FieldTypeDescription
urlstring
see description
A link to one video. One of url, urls or sourceStoragePath is required.
urlsarray of strings
see description
Up to 10 links, queued as a batch with one job per link. Takes priority over url. If any link is not supported, the whole batch is refused.
sourceStoragePathstring
see description
The path from Create a source upload URL, after you have uploaded the file.
sourceTitlestring
optional
Title for an uploaded file, up to 300 characters. Ignored for links.
sourceChannelstring
optional
Channel name for an uploaded file, up to 300 characters. Default Upload.
numClipsinteger
optional
1 to 20. Default 3.
minDurationinteger
optional
Shortest clip in seconds, 5 to 600. Default 30.
maxDurationinteger
optional
Longest clip in seconds, 5 to 600. Default 90.
categorystring
optional
generic (default), meme, sports or movies.
layoutstring
optional
blur (default) keeps the whole frame with a blurred fill above and below. crop zooms into the middle. smart follows the face. speaker and smart_crop are also accepted.
captionStylestring
optional
classic (default), karaoke (words light up as spoken), bold, minimal, neon or none.
aspectRatiostring
optional
9:16 (default), 1:1, 16:9 or 4:5.
userPromptstring
optional
Extra instructions for the clip finder, up to 2,000 characters.
optionsobject
optional
Render and selection options. See the table below.

Options

FieldTypeDescription
languagestring
optional
Spoken language of the source as a language tag (en, es, pt-BR), or auto to detect it.
formatstring
optional
What kind of video it is, which sets the rules for picking moments: generic, podcast (interviews and talking heads), reaction (a streamer reacting to a game or video), casino (slots or casino play), broadcast (sports with no reaction cam), meme or cinematic.
modestring
optional
captions burns captions on the whole video (up to 15 minutes) as one clip instead of finding moments. numClips becomes 1, and captionStyle cannot be none.
speaker_trackingboolean
optional
Reframe to follow the active speaker on videos with several people.
auto_zoomboolean
optional
Punch-in zooms on emphasis.
emoji_captionsboolean
optional
Add emoji to caption keywords.
remove_fillersboolean
optional
Cut filler words and long silences.
visual_selectionboolean
optional
Also look for moments with no speech, using vision analysis.
multicamboolean
optional
Alternate wide and tight framing like a second camera.
sfx_hitsboolean
optional
Whoosh and a small shake on punch-in moments.
cutawaysboolean
optional
Cut away to b-roll picked from the source video itself.
include_logoboolean
optional
Burn the active brand's logo into the clips. Quietly turned off when the brand has no logo.
brand_kitobject
optional
Caption styling: primary and secondary as #rgb or #rrggbb, and font (a font family name, up to 64 characters).
quality.brand_safetystring
optional
standard or gambling. Gambling leaves out rival brands, sexual content, addiction, anything involving minors, and complaints that a site does not pay.
quality.client_rulesstring
optional
Your own rules for picking clips, up to 2,000 characters. The moment picker follows them.
quality.banned_wordsarray of strings
optional
Up to 50 words or phrases (60 characters each). A clip in which one is spoken is dropped.
quality.vocabularyarray of strings
optional
Up to 100 correct spellings of names, brands and terms the captions must get right.
quality.dictionaryobject
optional
Up to 100 caption fixes, mapping what the transcript tends to hear to the right spelling, for example {"prolific": "Prolifik"}.
quality.key_momentsstring
optional
auto (default) gives goals, red cards, big wins and similar moments their own clip. off drops that guarantee.

Request

curl -X POST https://app.getprolifik.com/api/v1/clipper/jobs \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: clip-2026-10-13-ep42" \
  -d '{
    "url": "https://www.youtube.com/watch?v=aBcDeFgHiJk",
    "numClips": 5,
    "captionStyle": "karaoke",
    "options": { "format": "podcast", "quality": { "vocabulary": ["Prolifik"] } }
  }'

Response 202

{
  "job_id": "3f9a1c2b7d4e",
  "status": "queued",
  "created_at": "2026-10-13T09:12:44.512Z"
}

A batch (urls) returns { "batch_id": "...", "jobs": [{ "job_id", "status", "created_at" }] }. Each link becomes its own job and is billed separately, duplicates included.

Errors: 400 for an unsupported link or a missing source; 402 quota_exceeded when fewer than 5 clip minutes are left; 402 subscription_required without a paid plan; 413 when an uploaded source is over 8 GB; 429 past 10 jobs a minute per key or 30 an hour per account.

Get the clip-minute balance

GET/clipper/minutes

  • Scope clipper:read
  • Returns 200

Clip minutes left in the current billing period, the plan's allowance, and whether a new job would be accepted. Plans include 60 minutes a month on Lite, 360 on Starter and 1,400 on Pro. Enterprise is unmetered and reports -1 with unlimited: true.

Request

curl https://app.getprolifik.com/api/v1/clipper/minutes \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "clipMinutes": {
    "balance": 318.5,
    "allowance": 360,
    "unlimited": false,
    "plan": "starter",
    "minimumJobMinutes": 5,
    "canQueueJob": true
  }
}

canQueueJob only compares the balance with minimumJobMinutes. A job can still be refused for other reasons, such as an unpaid plan.

Get a clip job and its clips

GET/clipper/jobs/{jobId}

  • Scope clipper:read
  • Returns 200

One job, the settings it ran with, and every clip it produced, in order. Clip media links can expire, so read the job again for fresh links rather than storing them.

Path

FieldTypeDescription
jobIdstring
required
The job_id from create or list.

Request

curl https://app.getprolifik.com/api/v1/clipper/jobs/3f9a1c2b7d4e \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "job_id": "3f9a1c2b7d4e",
  "status": "completed",
  "source": {
    "url": "https://www.youtube.com/watch?v=aBcDeFgHiJk",
    "title": "How we got our first 1,000 users",
    "channel": "Acme Podcast",
    "duration": 2841
  },
  "progress": { "step": 4, "total_steps": 4, "current_step_name": "completed", "elapsed_seconds": 499 },
  "num_clips": 3,
  "min_duration": 30,
  "max_duration": 90,
  "category": "generic",
  "layout": "blur",
  "caption_style": "karaoke",
  "aspect_ratio": "9:16",
  "user_prompt": "",
  "auto_publish": false,
  "options": { "format": "podcast" },
  "clips": [
    {
      "clip_id": "3f9a1c2b7d4e_clip_000",
      "file_url": "https://...",
      "thumbnail_url": "https://...",
      "start_time": 412.3,
      "end_time": 468.9,
      "duration": 56.6,
      "title": "The cold email that got us 300 users",
      "description": "The founder explains the one-line email ...",
      "hook": "We sent one email and got 300 signups.",
      "hashtags": ["#startup", "#growth"],
      "virality_score": 82,
      "status": "completed",
      "error": null,
      "qc": {
        "status": "passed",
        "checks": [{ "id": "start_sentence", "ok": true, "detail": null }]
      },
      "key_moment": null
    }
  ],
  "created_at": "2026-10-13T09:12:44.512Z",
  "completed_at": "2026-10-13T09:21:03.118Z",
  "processing_time_seconds": 499,
  "error": null
}

Here num_clips is the number you asked for. Each clip also carries long_description, overlay_text, score_breakdown, tags, rating, overlays and logo fields, trimmed above. qc is the post-render check (passed, warning or failed, with one entry per check); it is null on older clips, and a failed clip is never posted automatically. key_moment is { kind, label, t } for a goal, red card, penalty, big win, bonus or peak, or null.

A missing job, or one owned by another account, returns 404.

Cancel a clip job

DELETE/clipper/jobs/{jobId}

  • Scope clipper:write
  • Returns 200

Stops a job that is still queued or running.

Path

FieldTypeDescription
jobIdstring
required
The job to cancel.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/clipper/jobs/3f9a1c2b7d4e \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true }

Returns 409 when the job is no longer running. The same 409 comes back for an ID that does not exist.

Poll clip job status

GET/clipper/jobs/{jobId}/status

  • Scope clipper:read
  • Returns 200

A light read for polling a running job. Status moves through queued, downloading, transcribing, selecting and producing to completed, failed or cancelled.

Path

FieldTypeDescription
jobIdstring
required
The job to check.

Request

curl https://app.getprolifik.com/api/v1/clipper/jobs/3f9a1c2b7d4e/status \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "status": "selecting",
  "progress": {
    "step": 3,
    "total_steps": 4,
    "current_step_name": "selecting",
    "elapsed_seconds": 214,
    "percent": 40
  },
  "clipCount": 0
}

progress.message and progress.percent appear only when the worker reports them. clipCount counts the clips that exist so far.

Create a source upload URL

POST/clipper/upload-url

  • Scope clipper:write
  • Returns 201

To clip a file instead of a link: ask for an upload URL, PUT the file's bytes to uploadUrl within 15 minutes, then create the job with sourceStoragePath set to the returned path. Accepted extensions are mp4, mov, webm, m4v, mkv and avi. A source must be under 8 GB, which is checked when you create the job.

Body

FieldTypeDescription
fileNamestring
optional
Default source.mp4. Characters other than letters, digits, dot, underscore and hyphen are replaced with _.
jobIdstring
optional
Reuse an ID from an earlier upload to put the file in the same folder. Omit it to get a new one.

Request

curl -X POST https://app.getprolifik.com/api/v1/clipper/upload-url \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "fileName": "episode-42.mp4" }'

# then upload the file
curl -X PUT --upload-file episode-42.mp4 "<uploadUrl>"

Response 201

{
  "jobId": "9b2e4f7a1c3d",
  "path": "<your user id>/9b2e4f7a1c3d/episode-42.mp4",
  "uploadUrl": "https://..."
}

The created clip job takes the upload's jobId as its job_id.

List clip publish destinations

GET/clipper/publish-destinations

  • Scope clipper:read
  • Returns 200

The connected social accounts a clip can be posted to. Without brandId it lists the account's active brand. A brand you do not own, or one your plan has locked, returns an empty list.

Query

FieldTypeDescription
brandIdUUID
optional
List another brand's accounts.

Request

curl "https://app.getprolifik.com/api/v1/clipper/publish-destinations?brandId=6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "destinations": [
    { "id": "8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "platform": "tiktok", "accountName": "acmehq" },
    { "id": "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b", "platform": "instagram", "accountName": "acme" }
  ]
}

Use an id as connectionId when you post a clip.

Publish or schedule a clip

POST/clipper/clips/{clipId}/publish

  • Scope clipper:write
  • Idempotency-Key optional
  • Returns 201

Creates a real post on a connected social account, now or at scheduledFor. The caption is built from the clip's title, description and hashtags. The account must be an active destination on one of your plan's brands.

A clip goes to the same account once: while a post of this clip to this account is scheduled, posting or already out, calling this again returns that post instead of creating another. Each account also has a daily post limit (20 on TikTok, 6 on YouTube, 100 elsewhere), counted per UTC day.

Path

FieldTypeDescription
clipIdstring
required
The clip_id from the job.

Body

FieldTypeDescription
connectionIdUUID
required
A destination id from List clip publish destinations.
scheduledForISO 8601 timestamp
optional
When to post, up to a year ahead. Omit to post now.

Request

curl -X POST https://app.getprolifik.com/api/v1/clipper/clips/3f9a1c2b7d4e_clip_000/publish \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: post-3f9a1c2b7d4e_clip_000-tiktok" \
  -d '{ "connectionId": "8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "scheduledFor": "2026-10-14T13:00:00Z" }'

Response 201

{
  "id": "c2a7e9d4-1f3b-4a8e-b6d0-5e9f2a1c7b38",
  "source": "clip",
  "job_id": "3f9a1c2b7d4e",
  "clip_id": "3f9a1c2b7d4e_clip_000",
  "account_id": "8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54",
  "platform": "tiktok",
  "account_handle": "acmehq",
  "title": "The cold email that got us 300 users",
  "description": "The cold email that got us 300 users ...",
  "published_at": "2026-10-13T09:30:12.004Z",
  "status": "pending",
  "external_post_id": null,
  "error": null,
  "attempts": 0,
  "scheduled_for": "2026-10-14T13:00:00.000Z",
  "completed_at": null
}

A post-now request comes back as publishing, a scheduled one as pending. Errors: 400 for a time in the past or more than a year out; 402 when connectionId is not an active destination; 404 for an unknown clip; 409 when the account needs reconnecting, the clip has no media yet, or the account hit its daily limit.

List clip publish records

GET/clipper/publishes

  • Scope clipper:read
  • Returns 200

Your most recent clip posts, newest first, up to 200. This list is not paginated.

Query

FieldTypeDescription
platformstring
optional
instagram, tiktok, youtube, x or linkedin. Exact match.
statusstring
optional
pending, publishing, published, failed or cancelled. Other values match nothing.
jobIdstring
optional
Only posts of clips from this job.

Request

curl "https://app.getprolifik.com/api/v1/clipper/publishes?status=failed" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "publishes": [
    {
      "id": "c2a7e9d4-1f3b-4a8e-b6d0-5e9f2a1c7b38",
      "source": "clip",
      "job_id": "3f9a1c2b7d4e",
      "clip_id": "3f9a1c2b7d4e_clip_000",
      "platform": "tiktok",
      "account_handle": "acmehq",
      "status": "failed",
      "error": "Publishing failed",
      "published_at": "2026-10-13T09:30:12.004Z"
    }
  ]
}

Each record has the same fields as the publish response, trimmed above. Older records from before the current posting pipeline have source: "legacy".

Retry a failed clip post

POST/clipper/publishes/{publishId}/retry

  • Scope clipper:write
  • Returns 200

Sends a failed clip post again. If the platform reports that the earlier attempt did go out, Prolifik keeps that post and does not post the clip twice.

Path

FieldTypeDescription
publishIdstring
required
The id of a failed publish record.

Request

curl -X POST https://app.getprolifik.com/api/v1/clipper/publishes/c2a7e9d4-1f3b-4a8e-b6d0-5e9f2a1c7b38/retry \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true }

Returns 409 when the post did not fail, already went out, or the account needs reconnecting, and 404 for an unknown ID.

Brands

A brand holds the voice, audience, colors, logo and posting schedule every piece is written and styled with. Lite covers 1 brand, Starter 3, Pro and Enterprise any number; brands past the limit stay readable but are locked for new work.

List brands

GET/brands

  • Scope content:read
  • Returns 200

Every brand in the workspace, oldest first, with isActive showing which ones your plan covers (Lite covers 1 brand, Starter 3, Pro and Enterprise all). activeBrandId is the brand selected in the dashboard.

Request

curl https://app.getprolifik.com/api/v1/brands \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "brands": [
  {
    "id": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "name": "Acme",
    "websiteUrl": "https://acme.com",
    "voice": "Plain, confident, a little dry. Short sentences.",
    "primaryColors": ["#0B1220", "#2563EB"],
    "logoUrl": null,
    "description": "Acme makes invoicing software for freelancers.",
    "targetAudience": "Freelance designers and developers who hate chasing payments.",
    "enrichmentStatus": "complete",
    "postingSchedule": { "days": [1, 3, 5], "times": ["09:00", "17:30"], "timezone": "Europe/Vilnius" },
    "isActive": true,
    "createdAt": "2026-09-02T08:14:51.220Z"
  }
  ],
  "activeBrandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f"
}

enrichmentStatus is pending while Prolifik is still reading the website, then complete or failed.

Create a brand

POST/brands

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Creates a brand from its website. Prolifik reads the page for a name, colors and a description straight away, then works out the voice and audience in the background (enrichmentStatus goes from pending to complete). Read the brand again to see the result.

If you already have a brand on the same hostname, that brand comes back with status 200 and deduped: true instead of a new one.

Body

FieldTypeDescription
urlstring
required
The brand's website, with http:// or https://. Up to 2,048 characters.

Request

curl -X POST https://app.getprolifik.com/api/v1/brands \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: brand-acme-com" \
  -d '{ "url": "https://acme.com" }'

Response 202

{
  "brand": {
    "id": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "name": "Acme",
    "websiteUrl": "https://acme.com",
    "voice": "",
    "enrichmentStatus": "pending",
    "isActive": true,
    ...
  },
  "deduped": false
}

Errors: 400 for a URL without an http(s) scheme; 402 quota_exceeded when the plan's brand limit is reached; 429 past 5 brands a minute; 503 when the background worker is unavailable (the brand is kept with enrichmentStatus: "failed", and a retry with the same key returns it).

Get a brand

GET/brands/{brandId}

  • Scope content:read
  • Returns 200

One brand's voice, audience, colors, logo and posting schedule. Brands your plan has locked can still be read.

Path

FieldTypeDescription
brandIdUUID
required
The brand.

Request

curl https://app.getprolifik.com/api/v1/brands/6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "brand": {
    "id": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "name": "Acme",
    "websiteUrl": "https://acme.com",
    "voice": "Plain, confident, a little dry. Short sentences.",
    "primaryColors": ["#0B1220", "#2563EB"],
    "logoUrl": null,
    "description": "Acme makes invoicing software for freelancers.",
    "targetAudience": "Freelance designers and developers who hate chasing payments.",
    "enrichmentStatus": "complete",
    "postingSchedule": { "days": [1, 3, 5], "times": ["09:00", "17:30"], "timezone": "Europe/Vilnius" },
    "isActive": true,
    "createdAt": "2026-09-02T08:14:51.220Z"
  }
}

Update a brand

PATCH/brands/{brandId}

  • Scope content:write
  • Returns 200

Changes the fields you send and leaves the rest alone. Send at least one. A brand your plan has locked returns 402. Changes made while enrichmentStatus is still pending are overwritten when the analysis finishes, so wait for complete.

Path

FieldTypeDescription
brandIdUUID
required
The brand.

Body

FieldTypeDescription
namestring or null
optional
Up to 200 characters.
voicestring or null
optional
How the brand talks, up to 5,000 characters.
primaryColorsarray of strings
optional
Up to 10 colors. Replaces the whole list.
logoUrlstring or null
optional
Up to 2,048 characters.
descriptionstring
optional
Up to 5,000 characters.
targetAudiencestring
optional
Up to 5,000 characters.
postingScheduleobject or null
optional
days (0 to 6, Sunday is 0), times (HH:MM, 24-hour) and timezone (an IANA zone such as America/New_York). null clears it.

Request

curl -X PATCH https://app.getprolifik.com/api/v1/brands/6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "voice": "Plain, confident, a little dry. Short sentences.",
    "postingSchedule": { "days": [1, 3, 5], "times": ["09:00"], "timezone": "Europe/Vilnius" }
  }'

Response 200

{
  "brand": { "id": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f", "name": "Acme", ... }
}

Content sources

A content source is a page Prolifik has read and summarized, such as a blog post or product page, so a content job can write about it.

List content sources

GET/content/sources

  • Scope content:read
  • Returns 200

Pages a brand can base new content on, newest first. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
brandIdUUID
required
The brand.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/content/sources?brandId=6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "sources": [
  {
    "id": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "url": "https://acme.com/blog/late-invoices",
    "title": "Why invoices get paid late",
    "notes": null,
    "status": "ready",
    "analysis": {
      "sourceType": "article",
      "mainTopic": "Late invoice payments",
      "argument": "Most late payments come from unclear terms, not bad clients.",
      "keyPoints": ["Put the due date in the subject line", "Send the reminder before the due date"],
      "audienceTakeaway": "Clear terms get you paid faster.",
      "tone": "practical",
      "targetAudience": "Freelancers"
    },
    "error": null,
    "lastAnalyzedAt": "2026-10-13T09:40:02.118Z",
    "createdAt": "2026-10-13T09:39:20.501Z",
    "updatedAt": "2026-10-13T09:40:02.118Z"
  }
  ],
  "hasMore": false,
  "nextCursor": null
}

Add a content source

POST/content/sources

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Queues a page (a blog post, product page, competitor page or video page) to be read and analyzed, and returns the source with status: "pending". Poll it until status is ready or failed, then pass its ID as contentSourceId when you create a content job. A brand can have up to 50 sources that are pending or ready.

Body

FieldTypeDescription
brandIdUUID
required
A brand your plan covers.
urlstring
required
The page. https:// is added if you leave the scheme off.
titlestring
optional
Up to 300 characters. Defaults to the hostname.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/sources \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: src-late-invoices" \
  -d '{ "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f", "url": "https://acme.com/blog/late-invoices" }'

Response 202

{
  "source": { "id": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d", "status": "pending", "analysis": null, ... }
}

Errors: 402 when the brand is locked by your plan; 404 for an unknown brand; 409 at 50 sources; 429 past 10 a minute (shared with re-analyze); 503 when the worker is unavailable. A page that cannot be fetched shows up later as status: "failed", not as an error here.

Get a content source

GET/content/sources/{sourceId}

  • Scope content:read
  • Returns 200

One source with its analysis status. When status is failed, error reads "Content source analysis failed."

Path

FieldTypeDescription
sourceIdUUID
required
The source.

Request

curl https://app.getprolifik.com/api/v1/content/sources/a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "source": {
    "id": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "url": "https://acme.com/blog/late-invoices",
    "title": "Why invoices get paid late",
    "notes": null,
    "status": "ready",
    "analysis": {
      "sourceType": "article",
      "mainTopic": "Late invoice payments",
      "argument": "Most late payments come from unclear terms, not bad clients.",
      "keyPoints": ["Put the due date in the subject line", "Send the reminder before the due date"],
      "audienceTakeaway": "Clear terms get you paid faster.",
      "tone": "practical",
      "targetAudience": "Freelancers"
    },
    "error": null,
    "lastAnalyzedAt": "2026-10-13T09:40:02.118Z",
    "createdAt": "2026-10-13T09:39:20.501Z",
    "updatedAt": "2026-10-13T09:40:02.118Z"
  }
}

analysis.sourceType is article, product-page, competitor, video or other. analysis.notableQuotes appears when the page has quotable lines.

Rename a source or edit its notes

PATCH/content/sources/{sourceId}

  • Scope content:write
  • Returns 200

Send title, notes or both.

Path

FieldTypeDescription
sourceIdUUID
required
The source.

Body

FieldTypeDescription
titlestring
optional
1 to 300 characters.
notesstring or null
optional
Up to 5,000 characters. null clears them.

Request

curl -X PATCH https://app.getprolifik.com/api/v1/content/sources/a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "notes": "Lead with the subject-line tip." }'

Response 200

{
  "source": { "id": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d", "notes": "Lead with the subject-line tip.", ... }
}

Delete a content source

DELETE/content/sources/{sourceId}

  • Scope content:write
  • Returns 200

Deletes the source. Content already made from it stays. Deleting a source that does not exist, or was already deleted, succeeds with alreadyDeleted: true.

Path

FieldTypeDescription
sourceIdUUID
required
The source.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/content/sources/a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "alreadyDeleted": false }

Re-analyze a content source

POST/content/sources/{sourceId}/reanalyze

  • Scope content:write
  • Returns 202

Sets the source back to pending and runs the analysis again. Send a new url to fix a wrong link; with the same URL, the page is not fetched again and only the analysis re-runs. The old analysis stays in the response until the new one lands.

Path

FieldTypeDescription
sourceIdUUID
required
The source.

Body (optional)

FieldTypeDescription
urlstring
optional
Replace the source URL before analyzing.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/sources/a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d/reanalyze \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 202

{
  "source": { "id": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d", "status": "pending", ... }
}

Errors: 402 when the brand is locked; 404 for an unknown source; 429 past 10 a minute (shared with add source); 503 when the worker is unavailable.

Content jobs and pieces

A content job makes a batch of rendered posts, called pieces, in one format. Pieces can be edited, re-rendered and deleted here, and posted from the Publishing endpoints.

List content jobs

GET/content/jobs

  • Scope content:read
  • Returns 200

Content production jobs, newest first, including jobs started in the app. Pieces are not included; read a single job for those. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
brandIdUUID
optional
Only this brand's jobs.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/content/jobs?brandId=6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "jobs": [
  {
    "id": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "contentSourceId": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d",
    "format": "caption-card",
    "requestedPieces": 5,
    "expectedPieces": 5,
    "generatedPieces": 5,
    "failedPieces": 0,
    "status": "ready_for_review",
    "deliveryMode": "download",
    "includeLogo": true,
    "mentionBrand": true,
    "createdAt": "2026-10-13T10:15:00.104Z",
    "updatedAt": "2026-10-13T10:19:42.877Z",
    "completedAt": "2026-10-13T10:19:42.877Z"
  }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a content job

POST/content/jobs

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Queues a batch of rendered posts in one of the six formats for a brand, and returns at once. Prolifik writes the copy from the brand (and the content source, if you pass one), picks visuals from its library, and renders each piece. Poll Get a content job: status moves from queued to generating, then to ready_for_review when at least one piece is ready (check failedPieces for the rest) or partially_failed when none came out. canceled means you cancelled it. Stop polling on any status other than queued or generating.

Each piece uses one render from the monthly budget; a job that needs more renders than you have left is refused before it starts (see usage). Only one job per format can be queued or generating at a time. Pieces you never download, schedule or post are deleted 14 days after they are made.

Formats: carousel (Image Carousel), kinetic-type (Text Story), scene-swap (Meme Reaction), b-roll (Clip Montage), caption-card (Caption Meme) and talking-head (Video Hook & Demo). talking-head plays a short hook clip, then your own product recording, so it needs demoVideoKey: a video from the media library or a demo upload, at most 100 MB and 60 seconds.

Body

FieldTypeDescription
brandIdUUID
required
A brand your plan covers.
formatstring
required
One of the six format IDs above.
countinteger
optional
Pieces to make, 1 to 50. Default 1.
contentSourceIdUUID
optional
A ready content source on the same brand to write from. Omit to write from the brand alone.
includeLogoboolean
optional
Put the brand logo on the pieces. Default false.
mentionBrandboolean
optional
Name the brand in the copy. Default true. Only carousels use this setting today.
customTextsarray
optional
Your own copy, used word for word instead of AI text. One entry per piece, in order, and no more entries than count. null lets AI write that piece. Carousels take { "slides": [...] } (up to 10 slides); every other format takes { "text": "..." } (up to 2,000 characters).
assetFiltersobject
optional
Narrow the library visuals and music. Which keys a format accepts is in the table below; caption-card accepts none.
deliveryModestring
optional
download (default) or schedule. Recorded as intent only; you still post each piece with Publish a piece.
demoVideoKeystring
see description
talking-head only, and required there. The product recording that plays after the hook.
hookVideoKeystring
optional
talking-head only. Your own hook clip of at most 15 seconds, for example an AI Studio talking head from the media library. Omit it and each piece gets a library hook.

assetFilters

FieldTypeDescription
themestring
optional
Formats: carousel, b-roll. A library category for the format, up to 64 characters. An unknown theme returns 400 with the valid ones.
personTagsarray
optional
Formats: carousel, kinetic-type, b-roll. Woman, Man, Animal, Landscape/Building.
audioMoodstring
optional
Formats: kinetic-type, b-roll, talking-head. none (silent), upbeat, chill, cinematic, motivational or dramatic.
memePersonTagsarray
optional
Formats: scene-swap. Only meme clips tagged Woman or Man.
backgroundPersonTagsarray
optional
Formats: scene-swap. Only background clips with these tags: Woman, Man, Animal, Landscape/Building.
memeReactionsarray
optional
Formats: scene-swap. Pick meme clips by reaction, and write the caption to set it up: smug_vindication, caught_in_the_act, instant_refusal, blank_incomprehension, quiet_trauma, two_block_reaction. When no clip fits, the whole pool is used.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/jobs \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: job-acme-captions-1013" \
  -d '{
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "contentSourceId": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d",
    "format": "caption-card",
    "count": 5,
    "includeLogo": true
  }'

Response 202

{
  "job": {
    "id": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
    "format": "caption-card",
    "requestedPieces": 5,
    "expectedPieces": null,
    "generatedPieces": 0,
    "failedPieces": 0,
    "status": "queued",
    ...
  }
}

Errors: 400 for a bad field or a talking-head video that fails its checks (the message says which); 402 quota_exceeded when the render budget is short or the brand is locked by your plan; 404 for an unknown brand or source; 409 when the source is not ready, a job for this format is already running, or the monthly job cap is reached; 429 past 5 jobs a minute; 503 when the worker is unavailable.

Get a content job

GET/content/jobs/{jobId}

  • Scope content:read
  • Returns 200

The job and every piece it has produced so far, oldest first, with signed media links. A job can move back to generating for a while after you regenerate one of its pieces.

Path

FieldTypeDescription
jobIdUUID
required
The job.

Request

curl https://app.getprolifik.com/api/v1/content/jobs/f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "job": {
    "id": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "contentSourceId": "a41d7c3e-8b2f-4e6a-9c1d-3f5e7a9b1c2d",
    "format": "caption-card",
    "requestedPieces": 5,
    "expectedPieces": 5,
    "generatedPieces": 5,
    "failedPieces": 0,
    "status": "ready_for_review",
    "deliveryMode": "download",
    "includeLogo": true,
    "mentionBrand": true,
    "createdAt": "2026-10-13T10:15:00.104Z",
    "updatedAt": "2026-10-13T10:19:42.877Z",
    "completedAt": "2026-10-13T10:19:42.877Z"
  },
  "pieces": [
    {
      "id": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
      "jobId": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
      "format": "caption-card",
      "status": "ready",
      "failureReason": null,
      "reviewStatus": "pending",
      "script": {
        "hook": "When the client says the invoice is \"in the system\" for the third week:",
        "body": "",
        "cta": "",
        "format": "caption-card"
      },
      "videoUrl": "https://...",
      "slideUrls": null,
      "thumbnailUrl": "https://...",
      "includeLogo": true,
      "origin": "pulse",
      "consumedAt": null,
      "expiresAt": "2026-10-27T10:15:00.104Z",
      "createdAt": "2026-10-13T10:15:00.104Z"
    }
  ]
}

Cancel a content job

DELETE/content/jobs/{jobId}

  • Scope content:write
  • Returns 200

Cancels a job that is queued or generating. Cancelling a job that is already cancelled also returns ok. Any other status returns 409.

Path

FieldTypeDescription
jobIdUUID
required
The job.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/content/jobs/f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true }

List pieces

GET/content/pieces

  • Scope content:read
  • Returns 200

Every generated piece on the account, newest first: pieces from content jobs, from Content Studio in the dashboard, and cloning variations. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
brandIdUUID
optional
Only pieces from this brand's jobs.
formatstring
optional
Exact format ID, for example carousel.
statusstring
optional
pending, pending_render, rendering, ready or failed.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/content/pieces?status=ready&limit=20" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "pieces": [
    {
      "id": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
      "jobId": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
      "format": "caption-card",
      "status": "ready",
      "failureReason": null,
      "reviewStatus": "pending",
      "script": {
        "hook": "When the client says the invoice is \"in the system\" for the third week:",
        "body": "",
        "cta": "",
        "format": "caption-card"
      },
      "videoUrl": "https://...",
      "slideUrls": null,
      "thumbnailUrl": "https://...",
      "includeLogo": true,
      "origin": "pulse",
      "consumedAt": null,
      "expiresAt": "2026-10-27T10:15:00.104Z",
      "createdAt": "2026-10-13T10:15:00.104Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "MjAyNi0xMC0xM1QxMDoxNTowMC4xMDQ1NjdafDFlM2E1YzdiLTlkMmYtNGI2ZS04YTFjLTNlNWY3YjlkMmE0Yw"
}

Get a piece

GET/content/pieces/{pieceId}

  • Scope content:read
  • Returns 200

One piece with its script and signed media links. A video piece has videoUrl; a carousel has slideUrls, one PNG per slide. Links expire within the hour, so fetch the piece again rather than storing them.

reviewStatus is pending, approved or rejected. consumedAt is set once the piece has been downloaded in the app, scheduled or posted; until then expiresAt says when an unused job piece is deleted.

Path

FieldTypeDescription
pieceIdUUID
required
The piece.

Request

curl https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "piece": {
      "id": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
      "jobId": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e",
      "format": "caption-card",
      "status": "ready",
      "failureReason": null,
      "reviewStatus": "pending",
      "script": {
        "hook": "When the client says the invoice is \"in the system\" for the third week:",
        "body": "",
        "cta": "",
        "format": "caption-card"
      },
      "videoUrl": "https://...",
      "slideUrls": null,
      "thumbnailUrl": "https://...",
      "includeLogo": true,
      "origin": "pulse",
      "consumedAt": null,
      "expiresAt": "2026-10-27T10:15:00.104Z",
      "createdAt": "2026-10-13T10:15:00.104Z"
    }
}

Edit a piece and render it again

PATCH/content/pieces/{pieceId}

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Changes the copy or logo setting and queues a new render, which uses one render from the budget. Returns the piece with status: "pending_render"; the old media links stay in the response until the new render lands. A piece that has been downloaded, scheduled or posted cannot be edited (409), and neither can one that is rendering.

Path

FieldTypeDescription
pieceIdUUID
required
The piece.

Body

FieldTypeDescription
hookstring
optional
Up to 2,000 characters; 220 for caption-card.
bodystring
optional
Up to 2,000 characters.
ctastring
optional
Up to 300 characters.
slidesarray of strings
optional
Carousel slide text: up to 10 slides of 2,000 characters.
includeLogoboolean
optional
Show or hide the brand logo.

Request

curl -X PATCH https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: edit-1e3a5c7b-1" \
  -d '{ "hook": "When the client says the invoice is \"being processed\" for week three:" }'

Response 202

{
  "piece": { "id": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c", "status": "pending_render", ... }
}

Errors: 402 when the render budget is used up or the brand is locked; 409 when the piece is consumed or rendering; 503 when the render worker is unavailable (the piece is marked failed and keeps the new script, so you can retry it).

Delete a piece

DELETE/content/pieces/{pieceId}

  • Scope content:write
  • Returns 200

Deletes the piece, its render and its publish records. Queued posts for it are cancelled first; if the posting provider refuses a cancel, nothing is deleted and you get 502. Posts that already went out stay up on the platforms. A piece that does not exist returns alreadyDeleted: true. A job piece that is still being made returns 409.

Path

FieldTypeDescription
pieceIdUUID
required
The piece.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "alreadyDeleted": false, "refundedPublishes": 1 }

refundedPublishes counts queued posts that were cancelled and returned to your monthly video quota.

Regenerate a failed piece

POST/content/pieces/{pieceId}/regenerate

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Replaces a failed piece from a content job with a newly written script and new library visuals. Uses one render. The replacement is a new piece with a new ID, and the old ID returns 404 once it is gone: read the job to find the new piece. To render the same script again instead, use Retry a failed render. No body needed.

Path

FieldTypeDescription
pieceIdUUID
required
A failed piece made by a content job.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c/regenerate \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Idempotency-Key: regen-1e3a5c7b-1"

Response 202

{ "queued": true, "pieceId": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c", "jobId": "f2c4e6a8-9b1d-4f3a-8c5e-7d9f1b3a5c6e" }

Returns 409 for a piece that has not failed or did not come from a content job.

Retry a failed render

POST/content/pieces/{pieceId}/retry

  • Scope content:write
  • Idempotency-Key required
  • Returns 202

Renders a failed piece's existing script again, under the same ID. Uses one render. Works on any failed piece. No body needed.

Path

FieldTypeDescription
pieceIdUUID
required
A failed piece.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c/retry \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Idempotency-Key: retry-1e3a5c7b-1"

Response 202

{
  "piece": { "id": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c", "status": "pending", ... }
}

Returns 409 for a piece that has not failed, 503 when the render worker is unavailable.

Publishing

Post pieces to the Instagram, TikTok, YouTube and X accounts connected to a brand, now or on a schedule, and manage posts that have not gone out yet. Accounts are connected in the dashboard, not through the API. Clips are posted from the Clipper endpoints, and cloned variations can be spread across accounts with Distribute.

List publish destinations

GET/publish-destinations

  • Scope content:read
  • Returns 200

The social accounts connected to a brand that your plan covers (Lite covers 1 account per platform, Starter 3, Pro and Enterprise all). Without brandId it uses the active brand. A brand you do not own, or one your plan has locked, returns an empty list. An account that needs reconnecting is still listed, and posting to it returns 400.

Query

FieldTypeDescription
brandIdUUID
optional
The brand.

Request

curl "https://app.getprolifik.com/api/v1/publish-destinations?brandId=6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "destinations": [
    { "id": "8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "platform": "tiktok", "accountName": "acmehq" },
    { "id": "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b", "platform": "instagram", "accountName": "acme" }
  ]
}

platform is instagram, tiktok, youtube or x. Use the id values as connectionIds.

Publish or schedule a piece

POST/content/pieces/{pieceId}/publish

  • Scope publish:write
  • Idempotency-Key required
  • Returns 201

Creates a real post on one or more connected accounts, now or at scheduledAt. Needs the publish:write scope, which is separate from content:write so a key can make content without being able to post it. The piece must be ready.

The first time a piece is scheduled or posted it counts as one of the plan's monthly videos (20 on Lite, 100 on Starter, 500 on Pro). A piece can have one live post at a time: while a post of it is scheduled or posting, another publish returns 409. The posted caption is your caption, a blank line, then the hashtags, each with a # added if missing. On X, the caption is cut to fit 280 characters and a carousel keeps its first 4 images.

Path

FieldTypeDescription
pieceIdUUID
required
A ready piece.

Body

FieldTypeDescription
brandIdUUID
required
The piece's brand.
connectionIdsarray of UUIDs
required
1 to 10 destination IDs from List publish destinations. A cloned variation posts to exactly one account.
captionstring
required
Up to 2,200 characters. Leave room for the hashtags: Instagram caps the whole caption at 2,200.
hashtagsarray of strings
optional
Up to 30, each up to 100 characters.
scheduledAtISO 8601 timestamp
optional
With Z or an offset, between one minute and one year ahead. Omit to post now.
aiGeneratedboolean
optional
Mark the post as AI-generated: TikTok's own AI label, and a "Made with AI" line on other platforms unless the caption already says so. Defaults to true when the piece uses AI Studio or AI influencer media.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/pieces/1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c/publish \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: publish-1e3a5c7b-oct14" \
  -d '{
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "connectionIds": ["8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b"],
    "caption": "Third week of \"it'"'"'s in the system\".",
    "hashtags": ["freelance", "invoicing"],
    "scheduledAt": "2026-10-14T13:00:00Z"
  }'

Response 201

{
  "publish": {
    "id": "4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f",
    "contentPieceId": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
    "platforms": ["tiktok", "instagram"],
    "connectionIds": ["8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b"],
    "caption": "Third week of \"it's in the system\".",
    "hashtags": ["freelance", "invoicing"],
    "scheduledAt": "2026-10-14T13:00:00.000Z",
    "postedAt": null,
    "status": "scheduled",
    "platformPostIds": null,
    "aiGenerated": false,
    "createdAt": "2026-10-13T10:25:31.660Z"
  }
}

A post-now request comes back as posting. Errors: 400 for a bad time, an account that is not on this brand or needs reconnecting; 402 quota_exceeded when the monthly videos are used up or a brand or account is locked by your plan; 404 for a piece that is not on this brand; 409 when the piece is not ready or already has a live post; 503 when the publishing worker is unavailable.

List content posts

GET/content/publishes

  • Scope content:read
  • Returns 200

Content posts, newest created first. Clip posts are listed under clip publish records. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
pieceIdUUID
optional
Only posts of this piece.
statusstring
optional
scheduled, posting, posted, partial, failed or cancelled.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/content/publishes?status=scheduled" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "publishes": [
  {
    "id": "4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f",
    "contentPieceId": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
    "platforms": ["tiktok", "instagram"],
    "connectionIds": ["8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b"],
    "caption": "Third week of \"it's in the system\".",
    "hashtags": ["freelance", "invoicing"],
    "scheduledAt": "2026-10-14T13:00:00.000Z",
    "postedAt": null,
    "status": "scheduled",
    "platformPostIds": null,
    "aiGenerated": false,
    "createdAt": "2026-10-13T10:25:31.660Z"
  }
  ],
  "hasMore": false,
  "nextCursor": null
}

Get a content post

GET/content/publishes/{publishId}

  • Scope content:read
  • Returns 200

One post and its status: scheduled, posting, posted, partial (some accounts posted, others failed), failed or cancelled. postedAt is set once it went out. platformPostIds holds what the posting provider reported, including each platform's post ID and URL on success or the error on failure.

Path

FieldTypeDescription
publishIdUUID
required
The post.

Request

curl https://app.getprolifik.com/api/v1/content/publishes/4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "publish": {
    "id": "4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f",
    "contentPieceId": "1e3a5c7b-9d2f-4b6e-8a1c-3e5f7b9d2a4c",
    "platforms": ["tiktok", "instagram"],
    "connectionIds": ["8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b"],
    "caption": "Third week of \"it's in the system\".",
    "hashtags": ["freelance", "invoicing"],
    "scheduledAt": "2026-10-14T13:00:00.000Z",
    "postedAt": null,
    "status": "scheduled",
    "platformPostIds": null,
    "aiGenerated": false,
    "createdAt": "2026-10-13T10:25:31.660Z"
  }
}

Edit a queued post's caption

PATCH/content/publishes/{publishId}

  • Scope publish:write
  • Returns 200

Changes the caption and hashtags of a post that has not gone out yet. Send both caption and hashtags in every update: the caption at the platform is rebuilt from the fields in the request, so a field you leave out is dropped there. Posts that went out, even partly, or were cancelled return 409. Failed posts can be edited.

Path

FieldTypeDescription
publishIdUUID
required
The post.

Body

FieldTypeDescription
captionstring
see description
Up to 2,200 characters.
hashtagsarray of strings
see description
Up to 30, each up to 100 characters.

Request

curl -X PATCH https://app.getprolifik.com/api/v1/content/publishes/4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "caption": "Week three of \"it'"'"'s in the system\".", "hashtags": ["freelance", "invoicing"] }'

Response 200

{
  "publish": { "id": "4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f", "caption": "Week three of \"it's in the system\".", ... }
}

Returns 502 when the posting provider rejects the change; the original caption is kept.

Cancel a queued post

DELETE/content/publishes/{publishId}

  • Scope publish:write
  • Returns 200

Cancels a post that has not gone out. refunded is true when the monthly video it used came back to your quota. Cancelling a post that is already cancelled returns alreadyCancelled: true. Posts that went out, even partly, return 409.

Path

FieldTypeDescription
publishIdUUID
required
The post.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/content/publishes/4b6d8f1a-3c5e-4a7b-9d2f-6e8a1c3b5d7f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "refunded": true, "alreadyCancelled": false }

Returns 502 when the posting provider could not cancel it; the post is left as it was, so try again.

Usage

How much of the month's videos and renders are left. Clip minutes and AI credits have their own endpoints: clip minutes and AI credits.

Get plan usage

GET/usage

  • Scope content:read
  • Returns 200

This billing month's numbers. videos counts videos you keep: downloaded in the app, scheduled or published, each piece once (20 a month on Lite, 100 on Starter, 500 on Pro). renders counts every successful render, including retries, regenerations and edits (1,000 on Lite, 2,000 on Starter, 4,000 on Pro). A content job that needs more renders than renders.remaining is refused with 402.

limit and remaining are null when unlimited (Enterprise), and 0 when the account has no paid plan.

Request

curl https://app.getprolifik.com/api/v1/usage \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "plan": "starter",
  "trial": null,
  "videos": {
    "used": 37,
    "limit": 100,
    "remaining": 63,
    "resetsAt": "2026-11-02T08:14:51.000Z",
    "note": "Counts videos you keep: downloaded in the app, scheduled or published. Each piece counts once."
  },
  "renders": {
    "used": 412,
    "limit": 2000,
    "remaining": 1588,
    "resetsAt": "2026-11-02T08:14:51.000Z",
    "note": "Counts every successful render: job pieces, retries, regenerations, and Studio saves and downloads. Cloning is paid in AI credits instead."
  }
}

trial is always null for API keys, which need a paid plan.

Media

Your media library, and upload links for the recordings the Video Hook & Demo format plays.

List the media library

GET/content/media

  • Scope content:read
  • Returns 200

Videos (the default) or images in your Prolifik media library, newest first: files you uploaded in the app, demo recordings saved from Content Studio, and AI Studio outputs when the key also has studio:read. A video with usableForHookDemo: true can go straight into a content job as demoVideoKey or hookVideoKey, so a recording that is already in the app never needs uploading again.

previewUrl and thumbnailUrl are signed and expire within the hour. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
typestring
optional
video (default) or image.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/content/media?type=video&limit=10" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "media": [
    {
      "id": "5c8e1f3a-7b2d-4a9e-8f1c-2d3e4f5a6b7c",
      "name": "dashboard-demo.mp4",
      "key": "uploads/<your user id>/dashboard-demo.mp4",
      "type": "video",
      "category": "demo",
      "description": null,
      "usableForHookDemo": true,
      "previewUrl": "https://...",
      "thumbnailUrl": "https://...",
      "createdAt": "2026-10-10T15:02:33.871Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

category is upload, demo, ai-studio or null. Files sent through Create a demo upload URL do not appear here; use the key you got back.

Create a demo upload URL

POST/content/uploads

  • Scope content:write
  • Returns 201

For the Video Hook & Demo format (talking-head). PUT the video to uploadUrl within 30 minutes with the returned contentType as the Content-Type header (it is part of the signature) and no Authorization header. Then pass key to Create a content job as demoVideoKey or hookVideoKey.

The file is checked when the job is created: an mp4, mov or webm of at most 100 MB, a demo of at most 60 seconds and a hook of at most 15 seconds. A file that fails the check is deleted. The render replaces the recording's own sound with a library music track.

Body

FieldTypeDescription
contentTypestring
required
video/mp4, video/quicktime or video/webm.

Request

curl -X POST https://app.getprolifik.com/api/v1/content/uploads \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contentType": "video/mp4" }'

# then upload the file
curl -X PUT -H "Content-Type: video/mp4" --upload-file demo.mp4 "<uploadUrl>"

Response 201

{
  "key": "uploads/<your user id>/api-0b6f2c4e-1d3a-4f5b-9c7e-8a2d4f6b1e3c.mp4",
  "uploadUrl": "https://...",
  "contentType": "video/mp4",
  "expiresInSeconds": 1800,
  "maxBytes": 104857600
}

Limited to 20 upload URLs a minute per key.

AI Studio

Generate images, short videos and talking-head videos with AI models. Generations spend AI credits (250 a month on Starter, 1,000 on Pro, plus any packs you bought), charged when a job is accepted and refunded if it fails. Creating needs Starter or higher; reading works on any paid plan.

List AI Studio models and prices

GET/studio/models

  • Scope studio:read
  • Returns 200

Every image, video and talking-head model you can pick, with the aspect ratios, durations and resolutions it accepts and what it costs in AI credits. Read this before you queue a generation: modelKey and params must match a model's entry here. Prices are per image (creditsPerImage) or per second of video (creditsPerSecond, by resolution where a model offers a choice, and at the audio rate when generateAudio is on).

Request

curl https://app.getprolifik.com/api/v1/studio/models \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "models": [
    {
      "key": "seedance-2",
      "label": "Seedance 2.0",
      "kind": "video",
      "aspectRatios": ["9:16", "3:4", "1:1", "4:3", "16:9"],
      "durationsSec": [4, 5, 6, 7, 8, 9, 10],
      "resolutions": ["480p", "720p", "1080p"],
      "defaultResolution": "1080p",
      "supportsAspectRatio": true,
      "supportsGeneratedAudio": true,
      "supportsNumImages": false,
      "requiresReferenceImage": false,
      "creditsPerImage": null,
      "creditsPerSecond": 68,
      "creditsPerSecondByResolution": { "480p": 30, "720p": 30, "1080p": 68 },
      "creditsPerSecondWithAudio": null,
      "creditsPerSecondWithAudioByResolution": null,
      "maxDurationSecByResolution": null
    }
  ],
  "defaultVideoModelKey": "kling-v3-pro",
  "talkingHeadModelKey": "seedance-2-talking-head",
  "talkingHeadLanguages": ["English", "Spanish", "French", "..."],
  "influencerBaseImageCredits": 4
}

Fields that do not apply to a model are null. maxDurationSecByResolution is set where a resolution caps the length.

Get the AI credit balance

GET/studio/credits

  • Scope studio:read
  • Returns 200

The same numbers as Settings > AI Credits: balance, the plan's monthly allowance (250 on Starter, 1,000 on Pro, none on Lite), when the period ends, monthly versus carried-over credits, and the 20 most recent ledger entries. Enterprise reports balance: -1 with unlimited: true. Credit packs are bought in the dashboard.

Request

curl https://app.getprolifik.com/api/v1/studio/credits \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "credits": {
    "balance": 214,
    "unlimited": false,
    "plan": "starter",
    "allowance": 250,
    "periodEnd": "2026-11-02T08:14:51.000Z",
    "canBuyPacks": true,
    "monthly": { "granted": 250, "used": 36, "remaining": 214 },
    "carried": { "remaining": 0, "purchased": 0 }
  },
  "recentActivity": [
    {
      "id": "1b3d5f7a-9c2e-4a6b-8d1f-3e5a7c9b2d4f",
      "type": "spend-image",
      "delta": -8,
      "label": "Image generation",
      "detail": null,
      "createdAt": "2026-10-13T10:02:11.402Z"
    }
  ]
}

Ledger type values include monthly-grant, purchase, refund, spend-image, spend-video, spend-influencer-creation and refund-failed-gen.

Create a reference image upload URL

POST/studio/uploads

  • Scope studio:write
  • Returns 201

For image edits and image-to-video. PUT the image to uploadUrl within 15 minutes, then pass key as params.imageKey when you queue a generation. Needs Starter or higher.

Body

FieldTypeDescription
contentTypestring
required
image/png, image/jpeg or image/webp.

Request

curl -X POST https://app.getprolifik.com/api/v1/studio/uploads \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contentType": "image/png" }'

# then upload the image
curl -X PUT -H "Content-Type: image/png" --upload-file product.png "<uploadUrl>"

Response 201

{
  "key": "studio/<your user id>/uploads/9f1b3d5a-7c2e-4b6d-8a1f-3c5e7b9d2a4c.png",
  "uploadUrl": "https://...",
  "expiresInSeconds": 900
}

Limited to 20 upload URLs a minute per key.

List AI Studio generations

GET/studio/generations

  • Scope studio:read
  • Returns 200

Image, video and talking-head generations, newest first. While hasMore is true, pass nextCursor as before for the next page.

Query

FieldTypeDescription
statusstring
optional
queued, running, complete or failed.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/studio/generations?status=complete&limit=10" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "generations": [
  {
    "id": "e3b1c9a7-5d2f-4c8e-a1b3-9f7d5e3c1a2b",
    "influencerId": null,
    "kind": "image",
    "modelKey": "gpt-image-2",
    "prompt": "A freelancer at a sunny kitchen table, laptop open, relieved smile",
    "params": { "aspectRatio": "9:16", "numImages": 1 },
    "status": "complete",
    "outputs": [
      { "key": "studio/<your user id>/outputs/...png", "contentType": "image/png", "url": "https://..." }
    ],
    "creditsCharged": 8,
    "error": null,
    "createdAt": "2026-10-13T10:02:11.402Z",
    "completedAt": "2026-10-13T10:02:39.917Z"
  }
  ],
  "hasMore": false,
  "nextCursor": null
}

Queue an AI Studio generation

POST/studio/generations

  • Scope studio:write
  • Idempotency-Key required
  • Returns 202

Queues an image, video or talking-head generation and returns it with status: "queued". Credits are charged when the job is accepted and refunded if it fails. Poll Get a generation until status is complete or failed. Needs Starter or higher.

What each kind needs: image needs a prompt; edit models also need params.imageKey or an influencerId. video needs params.imageKey or an influencerId as the source image, and takes params.motionPrompt. talking-head needs params.script, an image or influencer, and takes params.language (one of talkingHeadLanguages). For video and talking-head, always send params.durationSec.

Body

FieldTypeDescription
kindstring
required
image, video or talking-head.
modelKeystring
required
A key from List AI Studio models of the same kind.
promptstring
see description
Up to 5,000 characters. Required for images.
influencerIdUUID
optional
Use an AI influencer's portrait as the reference image. The influencer needs a finished portrait.
paramsobject
required
Model settings. See the table below. An unknown key returns 400.

Params

FieldTypeDescription
aspectRatiostring
optional
One of the model's aspectRatios.
resolutionstring
optional
480p, 720p or 1080p, where the model offers a choice.
numImagesinteger
optional
1 or 4, on models with supportsNumImages.
templatestring
optional
none, iphone-selfie or shocked.
enhanceboolean
optional
Images only: rewrite the prompt into a detailed photo brief first. The rewritten prompt is added to params.enhancedPrompt.
imageKeystring
optional
A key from a reference upload, or an earlier output's key.
motionPromptstring
optional
Video only: how things should move, up to 5,000 characters.
durationSecinteger
optional
Video and talking-head: one of the model's durationsSec.
generateAudioboolean
optional
Video models with supportsGeneratedAudio; billed at the audio rate.
scriptstring
optional
Talking-head: what the person says, up to 5,000 characters.
languagestring
optional
Talking-head: one of talkingHeadLanguages. Default English.
captionsboolean
optional
Talking-head: burn in captions. Default true.

Request

curl -X POST https://app.getprolifik.com/api/v1/studio/generations \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: gen-kitchen-table-001" \
  -d '{
    "kind": "image",
    "modelKey": "gpt-image-2",
    "prompt": "A freelancer at a sunny kitchen table, laptop open, relieved smile",
    "params": { "aspectRatio": "9:16", "numImages": 1 }
  }'

Response 202

{
  "generation": { "id": "e3b1c9a7-5d2f-4c8e-a1b3-9f7d5e3c1a2b", "kind": "image", "status": "queued", "outputs": [], "creditsCharged": 8, ... }
}

Errors: 400 when a parameter does not fit the model (the message says which); 402 insufficient_credits with needed and balance; 402 subscription_required on Lite; 404 for an unknown influencer; 409 when the influencer has no portrait yet; 429 past 5 a minute; 503 when the worker is unavailable (credits refunded).

Get a generation

GET/studio/generations/{generationId}

  • Scope studio:read
  • Returns 200

One generation's status. When it is complete, outputs holds a signed url per file; links expire within the hour, so read the generation again for fresh ones. A failed generation keeps creditsCharged at what was charged and has a separate refund entry in the credit ledger. modelKey is the model that ran, which for an image with a reference is the model's edit variant.

Path

FieldTypeDescription
generationIdUUID
required
The generation.

Request

curl https://app.getprolifik.com/api/v1/studio/generations/e3b1c9a7-5d2f-4c8e-a1b3-9f7d5e3c1a2b \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "generation": {
    "id": "e3b1c9a7-5d2f-4c8e-a1b3-9f7d5e3c1a2b",
    "influencerId": null,
    "kind": "image",
    "modelKey": "gpt-image-2",
    "prompt": "A freelancer at a sunny kitchen table, laptop open, relieved smile",
    "params": { "aspectRatio": "9:16", "numImages": 1 },
    "status": "complete",
    "outputs": [
      { "key": "studio/<your user id>/outputs/...png", "contentType": "image/png", "url": "https://..." }
    ],
    "creditsCharged": 8,
    "error": null,
    "createdAt": "2026-10-13T10:02:11.402Z",
    "completedAt": "2026-10-13T10:02:39.917Z"
  }
}

Influencers

A reusable AI person, with a portrait you can use as the reference image in AI Studio generations. Creating and regenerating need Starter or higher and cost AI credits.

List AI influencers

GET/influencers

  • Scope studio:read
  • Returns 200

Your reusable AI influencers, newest first. Not paginated. status is pending_base while the portrait is being made, then base_ready, or failed.

Request

curl https://app.getprolifik.com/api/v1/influencers \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "influencers": [
  {
    "id": "7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "name": "Maya",
    "gender": "female",
    "age": 29,
    "ethnicity": "Lithuanian",
    "details": "Freckles, short dark hair, casual denim jacket",
    "status": "base_ready",
    "baseImageUrl": "https://...",
    "libraryImages": [],
    "creditsSpent": 4,
    "error": null,
    "createdAt": "2026-10-12T14:20:05.118Z",
    "updatedAt": "2026-10-12T14:20:41.730Z"
  }
  ]
}

Create an AI influencer

POST/influencers

  • Scope studio:write
  • Idempotency-Key required
  • Returns 202

Creates an influencer and generates its portrait, for 4 AI credits. Poll Get an influencer until status is base_ready, then use its ID as influencerId in generations. Needs Starter or higher.

Body

FieldTypeDescription
namestring
required
1 to 100 characters.
genderstring
required
male or female.
ageinteger
optional
18 to 60. Default 25.
ethnicitystring
optional
Up to 100 characters.
detailsstring
optional
Look and style, up to 1,000 characters.
brandIdUUID
optional
Defaults to the active brand.

Request

curl -X POST https://app.getprolifik.com/api/v1/influencers \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: influencer-maya-01" \
  -d '{ "name": "Maya", "gender": "female", "age": 29, "details": "Freckles, short dark hair, casual denim jacket" }'

Response 202

{
  "influencer": { "id": "7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b", "name": "Maya", "status": "pending_base", "baseImageUrl": null, ... }
}

Errors: 402 insufficient_credits or subscription_required; 404 for an unknown brand; 429 past 5 influencer changes a minute (create, regenerate and delete share the limit); 503 when the worker is unavailable (credits refunded).

Get an AI influencer

GET/influencers/{influencerId}

  • Scope studio:read
  • Returns 200

One influencer, with a signed baseImageUrl once the portrait exists. The link expires within the hour.

Path

FieldTypeDescription
influencerIdUUID
required
The influencer.

Request

curl https://app.getprolifik.com/api/v1/influencers/7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "influencer": {
    "id": "7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b",
    "brandId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
    "name": "Maya",
    "gender": "female",
    "age": 29,
    "ethnicity": "Lithuanian",
    "details": "Freckles, short dark hair, casual denim jacket",
    "status": "base_ready",
    "baseImageUrl": "https://...",
    "libraryImages": [],
    "creditsSpent": 4,
    "error": null,
    "createdAt": "2026-10-12T14:20:05.118Z",
    "updatedAt": "2026-10-12T14:20:41.730Z"
  }
}

Delete an AI influencer

DELETE/influencers/{influencerId}

  • Scope studio:write
  • Returns 200

Deletes the influencer and its stored images. Generations made with it stay. Credits are not refunded. Deleting one that does not exist succeeds with alreadyDeleted: true.

Path

FieldTypeDescription
influencerIdUUID
required
The influencer.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/influencers/7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "alreadyDeleted": false }

Returns 409 while the portrait is still being generated.

Regenerate an influencer portrait

POST/influencers/{influencerId}/regenerate

  • Scope studio:write
  • Idempotency-Key required
  • Returns 202

Replaces the portrait with a newly generated person, for 4 AI credits. Works on influencers that are base_ready or failed. Send a JSON body, even if it is just {}.

Path

FieldTypeDescription
influencerIdUUID
required
The influencer.

Body

FieldTypeDescription
instructionsstring
optional
What to change, up to 1,000 characters.

Request

curl -X POST https://app.getprolifik.com/api/v1/influencers/7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b/regenerate \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: influencer-maya-regen-01" \
  -d '{ "instructions": "Older, around 35, glasses" }'

Response 202

{ "ok": true, "influencerId": "7a9c1e3b-2d4f-4a6c-8e1b-3d5f7a9c1e2b" }

Returns 409 while a portrait is already being generated, 402 when credits run short.

Cloning

Rebuild a TikTok or Instagram photo carousel or text-over-video post as variations in your brand's voice, translate them, and schedule them across accounts. Every cloning endpoint, reads included, needs Starter or higher; work is paid in AI credits.

Quote a clone

POST/cloning/estimate

  • Scope studio:read
  • Returns 200

What cloning a post would cost, without downloading or charging anything. The quote is 10 credits for download and analysis plus 4 credits per new image. Before the post is read, a photo post's slide count is unknown, so it is quoted at six slides and the response sets assumedSlides. Needs Starter or higher.

Body

FieldTypeDescription
urlstring
required
A single TikTok or Instagram post link.
variationsinteger
optional
1 to 8. Default 3.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/estimate \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://www.tiktok.com/@acme/photo/7420000000000000000", "variations": 3 }'

Response 200

{
  "credits": 82,
  "breakdown": [
    { "label": "Download and analysis", "credits": 10 },
    { "label": "3 variations × 6 slides × 4 cr (estimated)", "credits": 72 }
  ],
  "assumedSlides": 6
}

List cloning jobs

GET/cloning/jobs

  • Scope studio:read
  • Returns 200

Cloning jobs, newest first. While hasMore is true, pass nextCursor as before for the next page. Needs Starter or higher.

Query

FieldTypeDescription
brandIdUUID
optional
Only this brand's jobs.
statusstring
optional
pending, ingesting, analysing, transforming, ready or failed.
beforestring
optional
The previous page's nextCursor. An ISO 8601 timestamp also works; see Pagination.
limitinteger
optional
1 to 100. Default 50.

Request

curl "https://app.getprolifik.com/api/v1/cloning/jobs?status=ready" \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "jobs": [
    {
      "id": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f",
      "brandProfileId": "6f0c2d9e-3a1b-4c7d-8e5f-1a2b3c4d5e6f",
      "url": "https://www.tiktok.com/@somecreator/photo/7420000000000000000",
      "platform": "tiktok",
      "sourceKind": "slideshow",
      "sourceFormat": "slideshow",
      "status": "ready",
      "errorMessage": null,
      "createdAt": "2026-10-13T11:00:04.210Z",
      "updatedAt": "2026-10-13T11:04:52.771Z",
      "creditsCharged": 82,
      "preview": { "coverUrl": "https://...", "title": "3 invoice mistakes", "uploader": "somecreator", "views": 184000 },
      "progress": null,
      "variationCount": 3
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Clone a TikTok or Instagram post

POST/cloning/jobs

  • Scope studio:write
  • Idempotency-Key required
  • Returns 202

Paste a TikTok or Instagram link to a photo carousel or a text-over-video post, and Prolifik rebuilds it as variations in the brand's voice: rewritten copy, slightly changed visuals, the brand's logo. Returns the job with status: "pending"; poll Get a cloning job until it is ready or failed.

The estimated credits are reserved up front. You pay for the analysis and each variation that is delivered; the rest is refunded when the job settles. A brand can have up to 50 cloning jobs that have not failed. Needs Starter or higher.

Body

FieldTypeDescription
urlstring
required
A single TikTok or Instagram post link.
brandIdUUID
optional
Whose voice and logo to use. Defaults to the active brand.
variationsinteger
optional
1 to 8. Default 3.
mentionBrandboolean
optional
Name the brand in the rewritten copy, not only in the logo.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/jobs \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: clone-7420000000000000000" \
  -d '{ "url": "https://www.tiktok.com/@somecreator/photo/7420000000000000000", "variations": 3 }'

Response 202

{
  "job": {
    "id": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f",
    "status": "pending",
    "creditsCharged": 0,
    "options": { "variations": 3 },
    "source": null,
    "analysis": null,
    "variations": [],
    ...
  }
}

Errors: 400 for a profile, story or other link that is not a single post; 402 insufficient_credits; 404 for an unknown brand; 409 at 50 jobs for the brand; 429 past 10 in 10 minutes; 503 when the worker is unavailable (credits refunded).

Get a cloning job and its variations

GET/cloning/jobs/{jobId}

  • Scope studio:read
  • Returns 200

The job, the source post as Prolifik read it (source, with signed media links), the analysis, and every variation built so far. A variation is a content piece: save it to the Library with Save a variation to schedule or post it like any other piece, or spread several across accounts with Distribute.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Request

curl https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{
  "job": {
    "id": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f",
    "status": "ready",
    "creditsCharged": 82,
    "progress": null,
    "variationCount": 3,
    "source": { "kind": "slideshow", "platform": "tiktok", "caption": "...", "images": [{ "index": 0, "url": "https://..." }] },
    "analysis": { ... },
    "variations": [
      {
        "pieceId": "d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c",
        "variationIndex": 0,
        "format": "carousel",
        "hook": "3 invoice mistakes that cost freelancers a month of rent",
        "thumbnailUrl": "https://...",
        "finalVideoUrl": null,
        "slideUrls": ["https://...", "https://..."],
        "status": "ready",
        "reviewStatus": "pending",
        "language": null,
        "localizedFrom": null,
        "translatable": true,
        "translateBlockedReason": null,
        "caption": "Stop losing money on late invoices.",
        "hashtags": ["#freelance", "#invoicing"],
        "schedules": []
      }
    ],
    ...
  }
}

While a job runs, progress is { step, total, label }. A carousel variation has slideUrls; any other format has finalVideoUrl. translatable says whether Translate variations can take it now.

Delete a cloning job

DELETE/cloning/jobs/{jobId}

  • Scope studio:write
  • Returns 200

Deletes a finished job (ready or failed); a running job returns 409. Anything still reserved is refunded first. The variations are kept as pieces, detached from the job.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Request

curl -X DELETE https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "jobId": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f" }

Add variations to a job

POST/cloning/jobs/{jobId}/variations

  • Scope studio:write
  • Idempotency-Key optional
  • Returns 202

Builds more variations on a job that is already analyzed, without downloading and reading the post again. The job goes back to transforming; poll it until ready. The reservation includes the 10-credit analysis line, which is refunded when the job settles because the analysis is not charged again.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Body

FieldTypeDescription
variationsinteger
required
1 to 8.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f/variations \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: clone-more-b8d2f4a6-1" \
  -d '{ "variations": 2 }'

Response 202

{ "ok": true, "jobId": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f", "variations": 2 }

Returns 409 while the job is still running, before it has been analyzed, or for a kind of post that cannot be cloned yet.

Translate variations

POST/cloning/jobs/{jobId}/localize

  • Scope studio:write
  • Idempotency-Key required
  • Returns 202

Each (variation, language) pair becomes a new variation on the same job, built on the original's background with no new images. App, product and brand names stay as they are; the caption and hashtags are translated. Costs 3 credits per translation, charged only for translations whose render finishes. At most 20 translations per request.

Languages: es (Spanish), pt-BR (Portuguese, Brazil), fr, de, it, nl, pl, tr, id (Indonesian). Pairs that already exist are skipped; if all of them exist, the request returns 409.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Body

FieldTypeDescription
languagesarray of strings
required
One or more language codes from the list above.
pieceIdsarray of UUIDs
optional
Up to 50 variations to translate. Omit to translate every finished original variation.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f/localize \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: clone-es-de-b8d2f4a6" \
  -d '{ "languages": ["es", "de"] }'

Response 202

{
  "ok": true,
  "jobId": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f",
  "translations": 2,
  "credits": 6,
  "items": [
    { "pieceId": "d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c", "language": "es", "variationIndex": 3 },
    { "pieceId": "d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c", "language": "de", "variationIndex": 4 }
  ]
}

Translated variations carry language and localizedFrom. A variation made before translation existed, edited since it was cloned, or itself a translation returns 409.

Retry a failed cloning job

POST/cloning/jobs/{jobId}/retry

  • Scope studio:write
  • Idempotency-Key optional
  • Returns 202

Starts a failed job with no variations again from the link. Returns 409 for a job that is not failed, one that already has variations (add variations instead), or a post Prolifik cannot clone yet, since a retry would charge again and fail the same way.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f/retry \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 202

{ "ok": true, "jobId": "b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f" }

Schedule variations across accounts

POST/cloning/jobs/{jobId}/distribute

  • Scope publish:write
  • Idempotency-Key required
  • Returns 200

Creates real posts. Spreads different variations of one job across the brand's social accounts at staggered times, so no two accounts post the same variation and each account gets at most one post from the job per day. Each post uses the variation's own caption and hashtags. Needs publish:write and a Starter or higher plan.

Send either an explicit plan, or socialAccountIds and startAt and let Prolifik plan it with the same rules the dashboard uses. The whole plan is checked before anything is scheduled: ownership, rendering, platform fit (a carousel cannot go to YouTube), duplicates, the time window and your monthly video quota. Surplus variations go out on the following days.

Path

FieldTypeDescription
jobIdUUID
required
The cloning job.

Body: explicit plan

FieldTypeDescription
planarray
see description
1 to 50 entries of { pieceId, socialAccountId, scheduledAt }. scheduledAt must be between one minute and one year ahead.

Body: server-planned

FieldTypeDescription
socialAccountIdsarray of UUIDs
see description
1 to 20 accounts from publish destinations, in posting order.
startAtISO 8601 timestamp
see description
When the first account posts.
spacingMinutesinteger
optional
Minutes between accounts, 0 to 1,440. Default 45.
jitterMinutesinteger
optional
Each time moves by up to this many minutes either way, 0 to 30. Default 10.
languagesarray of strings
optional
Which variations to hand out: original and/or language codes. By default originals go first, then translations.
timeZonestring
optional
IANA zone that "one post per account per day" is counted in. Default UTC.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/jobs/b8d2f4a6-1c3e-4b5d-9f7a-2c4e6a8b1d3f/distribute \
  -H "Authorization: Bearer $PROLIFIK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: distribute-b8d2f4a6-oct14" \
  -d '{
    "socialAccountIds": ["8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54", "0e7b3c1d-9f2a-4d8e-b5c6-7a1f2e3d4c5b"],
    "startAt": "2026-10-14T09:00:00Z",
    "timeZone": "Europe/Vilnius"
  }'

Response 200

{
  "results": [
    {
      "pieceId": "d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c",
      "socialAccountId": "8d1f5a2e-4b7c-4e19-9a3d-2c6b0f7e1a54",
      "scheduledAt": "2026-10-14T09:04:00.000Z",
      "status": "scheduled",
      "postId": "c2a7e9d4-1f3b-4a8e-b6d0-5e9f2a1c7b38"
    }
  ],
  "scheduled": 2,
  "failed": 0,
  "accounts": 2,
  "excluded": []
}

postId is a content publish record. In server-planned mode, excluded lists accounts that could take no variation and why, and accountsWithoutVariation lists accounts left over when variations ran out. An identical entry that already exists comes back with alreadyScheduled: true and is not sent again. Only one distribute per job runs at a time (409 in_progress).

Save a variation to the Library

POST/cloning/pieces/{pieceId}/library

  • Scope studio:write
  • Returns 200

Keeps one variation in the Library, so it can be scheduled or posted from the dashboard like any other piece. Its reviewStatus becomes approved. No body needed.

Path

FieldTypeDescription
pieceIdUUID
required
The variation's pieceId.

Request

curl -X POST https://app.getprolifik.com/api/v1/cloning/pieces/d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c/library \
  -H "Authorization: Bearer $PROLIFIK_API_KEY"

Response 200

{ "ok": true, "pieceId": "d4f6a8c2-3e5b-4d7f-8a1c-5e7b9d1f3a4c", "reviewStatus": "approved" }

Returns 409 when the variation has not finished rendering.

OpenAPI document

The machine-readable description of the API is at app.getprolifik.com/api/v1/openapi.json. It is OpenAPI 3.1, needs no key to fetch, and is generated from the same code as the API, so it always lists the live endpoints. Each operation names its scope in x-required-scope. Use it to generate a client or import the API into Postman or Insomnia. Its response schemas are loose in places; where they are, this page shows the real shapes.

curl https://app.getprolifik.com/api/v1/openapi.json -o prolifik-openapi.json

Support

Email info@getprolifik.com with the endpoint, the time of the call and the requestId from the error (the same value as the x-request-id header). For the product itself, start with the Quickstart or the FAQ.

Build on Prolifik

API keys come with every paid plan, from €20 a month, alongside the AI Clipper, auto-posting to four platforms and the MCP server.

See plans and pricing