Skip to content
Create a key

Paste a key to use it in the examples on these pages. The key stays in this browser tab. These pages never send it anywhere.

Estimate the cost before you spend credits

Some calls spend credits. Before you start one, you can ask what it costs and whether the account can pay for it. The estimate changes nothing and uses nothing.

This guide reads the balance, estimates the price of an analysis and starts the analysis only when the account can pay. The steps use variables, so an id from one answer feeds the next call. The examples read your key from the GPT_VIDEO_API_KEY variable, as Authentication explains.

Read the balance

get_credits (GET /v1/credits). Check the balance before an analysis.

Request example
curl "https://api.gpt-video.com/v1/credits" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY"
const res = await fetch("https://api.gpt-video.com/v1/credits", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
  },
});
const data = await res.json();
console.log(data);
import os

import requests

r = requests.get(
    "https://api.gpt-video.com/v1/credits",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
print(data)
{
  "method": "tools/call",
  "params": {
    "name": "get_credits",
    "arguments": {}
  }
}
Example response

GET /v1/credits

HTTP 200
{
  "plan": "creator",
  "planRequired": false,
  "credits": {
    "period": "2026-09",
    "periodEndsAt": "2026-10-01T00:00:00.000Z",
    "included": 800,
    "remaining": 1020,
    "remainingFromPlan": 770,
    "topUp": 250
  },
  "bonus": null,
  "dailyLimits": {
    "resetsAt": "2026-09-02T00:00:00.000Z",
    "exports": {
      "limit": 100,
      "used": 1,
      "remaining": 99
    },
    "assistantTurns": {
      "limit": 500,
      "used": 12,
      "remaining": 488
    },
    "toolRuns": {
      "limit": 100,
      "used": 2,
      "remaining": 98
    },
    "fakeTextScripts": {
      "limit": 100,
      "used": 0,
      "remaining": 100
    },
    "assistantTurnsThroughApi": {
      "limit": 100,
      "used": 4,
      "remaining": 96
    }
  }
}

credits.remaining is what the plan credits and the top-up can pay right now. The API bonus is not part of it. The bonus has its own block, bonus, with what is left and when it ends.

When planRequired is true, the account must choose a plan in the GPT-Video app before it can spend those credits. Through the API, plan credits and top-up credits are spent only on the Creator and Studio plans. API pricing explains what the API bonus pays.

dailyLimits shows the exports, Editor Assistant turns, tool runs and fake text scripts that are left today. They start again at 00:00 UTC. Exports use no credits, so check this block before you export.

Find the project

list_projects (GET /v1/projects). List the projects.

Request example
# This command needs jq to read the answer.
PROJECT_ID=$(curl -sS "https://api.gpt-video.com/v1/projects" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" | jq -r '.data[0].id')
const res = await fetch("https://api.gpt-video.com/v1/projects", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
  },
});
const data = await res.json();
const projectId = data.data[0].id;
import os

import requests

r = requests.get(
    "https://api.gpt-video.com/v1/projects",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
project_id = data["data"][0]["id"]
Example response

GET /v1/projects

HTTP 200
{
  "data": [
    {
      "id": "prj_1d8b6e0c47a24f93b1c2",
      "kind": "autoclip",
      "title": "Weekly podcast",
      "status": "draft",
      "sourceAssetId": null,
      "compositionId": null,
      "settings": {
        "language": null,
        "clipCount": 5,
        "minDurationSec": null,
        "maxDurationSec": null,
        "objective": "educational",
        "captionStyle": null,
        "reframe": null,
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [
        "language",
        "minDurationSec",
        "maxDurationSec",
        "captionStyle",
        "reframe"
      ],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:50:00.000Z",
      "updatedAt": "2026-09-01T09:50:00.000Z"
    },
    {
      "id": "prj_7c2e9a4f1b3d4e8096a5",
      "kind": "autoclip",
      "title": "Founder interview",
      "status": "ready",
      "sourceAssetId": "ast_3f9a1c7e5b2d4086a1c4",
      "compositionId": null,
      "settings": {
        "language": "en",
        "clipCount": 3,
        "minDurationSec": 15,
        "maxDurationSec": 60,
        "objective": "viral",
        "captionStyle": "word-pop",
        "reframe": "face",
        "instructions": null,
        "vocabulary": null
      },
      "missingSettings": [],
      "splitScreen": null,
      "streamer": null,
      "redditStory": null,
      "fakeText": null,
      "vibeEdit": null,
      "errorMessage": null,
      "createdAt": "2026-09-01T09:10:00.000Z",
      "updatedAt": "2026-09-01T09:40:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

The list shows the newest project first. Projects made in the GPT-Video app are listed too. The example keeps the first one in projectId. If you know the project you want, set projectId yourself.

Estimate the price

estimate_analysis (POST /v1/projects/{projectId}/analyze/estimate). Price an analysis before starting it.

Request example
curl -X POST "https://api.gpt-video.com/v1/projects/$PROJECT_ID/analyze/estimate" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY"
const res = await fetch(`https://api.gpt-video.com/v1/projects/${projectId}/analyze/estimate`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
  },
});
const data = await res.json();
console.log(data);
import os

import requests

r = requests.post(
    f"https://api.gpt-video.com/v1/projects/{project_id}/analyze/estimate",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
)
data = r.json()
print(data)
Example response

POST /v1/projects/prj_7c2e9a4f1b3d4e8096a5/analyze/estimate

HTTP 200
{
  "estimate": {
    "credits": 30,
    "creditsToCharge": 30,
    "sourceDurationSec": 1800,
    "runningJobId": null,
    "creditsAvailable": 640,
    "planRequired": false,
    "enough": true,
    "payer": "plan",
    "bonusRemaining": null
  }
}

The estimate works for the analysis of an Auto Clip project, for the build of a split screen, streamer or Reddit story project, and for the preparation of a Vibe Editing project. Fake text projects have no price here, because their story and voices use the daily fake text allowance. The answer is CONFLICT for them.

Read these fields of estimate.

  • credits is the price of a new run of the work.
  • creditsToCharge is what starting would charge now. It is 0 when the same work is already running, and runningJobId is that job.
  • enough is true when the start would not be refused for credits or for a plan.
  • payer says who would pay. It is plan or api_bonus, and it is null when the start would be refused.
  • bonusRemaining is what is left of the API bonus, or null when the account never had one.

The estimate refuses for the same reasons the start would. A project without a source video answers SOURCE_MISSING, and settings still to choose answer CONFLICT with the names of the settings. The settings do not change the price of an analysis, which depends only on the length of the video. To price a project whose settings are still open, choose them with update_project (PATCH /v1/projects/{projectId}), using the values that get_setting_options (GET /v1/reference/setting-options) lists, and estimate again. They can still change before the analysis starts. A project the estimate accepts is ready to start, and the price it gives is the price the start charges.

The API bonus pays for a whole price or for none of it. It never pays part of a price, and a price is never split between the bonus and the plan.

Start only when it is enough

If enough is false, do not start. Read Handle running out of credits to see what to do for each answer.

If enough is true, start the analysis.

analyze_project (POST /v1/projects/{projectId}/analyze). Analyze a project.

Request example
# This command needs jq to read the answer.
# Create the key once. Send this same key again if you retry this request.
IDEMPOTENCY_KEY=$(uuidgen)
JOB_ID=$(curl -sS -X POST "https://api.gpt-video.com/v1/projects/$PROJECT_ID/analyze" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" | jq -r '.job.id')
// Create the key once. Send this same key again if you retry this request.
const idempotencyKey = crypto.randomUUID();
const res = await fetch(`https://api.gpt-video.com/v1/projects/${projectId}/analyze`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
  },
});
const data = await res.json();
const jobId = data.job.id;
import os
import uuid

import requests

# Create the key once. Send this same key again if you retry this request.
idempotency_key = str(uuid.uuid4())
r = requests.post(
    f"https://api.gpt-video.com/v1/projects/{project_id}/analyze",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
)
data = r.json()
job_id = data["job"]["id"]
Example response

POST /v1/projects/prj_7c2e9a4f1b3d4e8096a5/analyze

HTTP 202
{
  "job": {
    "id": "job_b582bc8315e372bf9ba3",
    "kind": "analysis",
    "status": "queued",
    "progress": 0,
    "errorMessage": null,
    "projectId": "prj_7c2e9a4f1b3d4e8096a5",
    "compositionId": null,
    "exportId": null,
    "assetId": null,
    "toolRunId": null,
    "turnId": null,
    "createdAt": "2026-09-01T11:10:00.000Z",
    "startedAt": null,
    "finishedAt": null
  },
  "creditsCharged": 30,
  "reused": false,
  "paidWith": "plan"
}

creditsCharged is the price the estimate gave. If time has passed since the estimate, another charge from the API or from the app may have lowered the balance, so read the estimate again right before you start.

The estimate does not reserve credits. They are charged when the analysis starts. If the request times out, send it again with the same Idempotency-Key. A retry never charges twice, and it answers what was paid and by whom.

Follow the job with get_job (GET /v1/jobs/{jobId}). Turn a long video into clips shows the whole path, from the video to the MP4 file.