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.

Correct words in the captions

replace_caption_wordsPOST/v1/compositions/{compositionId}/captions/replaceBeta

Replaces words in the captions of a composition, such as a name the transcript got wrong.

Beta. This endpoint is supported and can gain new fields. If a change would break your code, we tell you before it takes effect and list it in the changelog.

About

Each replacement finds its word without minding capitals or punctuation, and keeps the punctuation of the word it replaces. With occurrence, only that occurrence of the word changes, counting from 1; without it, every one does. A word that is not in the captions is listed in notFound with up to three similar words, and nothing is written when no word was found. Send the revision you read; up to 30 replacements a request. Works on Auto Clip, streamer, split screen and Reddit story compositions; the captions of a Vibe Editing project are corrected with apply_timeline_operations (POST /v1/projects/{projectId}/timeline/operations).

Example use

A podcast team sees that the captions spell the name of a guest wrongly in every clip. A script sends the same replacement to each composition of the episode.

Endpoint

Code
POST https://api.gpt-video.com/v1/compositions/{compositionId}/captions/replace
  • Scope clips:write.
  • Rate limit 60 requests a minute for each key and 120 for each account, shared by every write call. See rate limits.
  • Idempotency-Key is optional.

Parameters

Path

NameTypeRequiredDescription
compositionIdstringRequiredThe id of the composition: accept_clip (POST /v1/clips/{clipId}/accept) returns it for a clip, and list_compositions (GET /v1/projects/{projectId}/compositions) or the project's compositionId give it for the other kinds of project. Matches ^cmp_[0-9a-f]{20}$.

Headers

NameTypeRequiredDescription
Idempotency-KeystringOptionalSend the same key on every retry of this request. From 1 to 255 visible characters, such as a UUID. See idempotency.

Body

NameTypeRequiredDescription
expectedRevisionintegerRequiredThe revision of the composition you read with get_composition (GET /v1/compositions/{compositionId}). If the composition changed since, the request answers CONFLICT and changes nothing. At least 0.
replacementsarray of objectsRequiredThe words to replace, from 1 to 30. When two replacements find the same word, the first one wins it. From 1 to 30 items.
replacements[].fromstringRequiredThe word to find, compared without capitals or punctuation. From 1 to 200 characters.
replacements[].tostringRequiredThe text to write in its place. From 1 to 200 characters.
replacements[].occurrenceintegerOptionalWhich occurrence of the word to replace, counting from 1. Leave it out to replace every one. At least 1.

Send only the fields listed here. Any other field is refused with UNKNOWN_PARAMETER.

Request

Correct a word everywhere.

Request example
curl -X POST "https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions/replace" \
  -H "Authorization: Bearer $GPT_VIDEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expectedRevision": 0,
    "replacements": [
      {
        "from": "customer",
        "to": "client"
      }
    ]
  }'
const res = await fetch("https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions/replace", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GPT_VIDEO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "expectedRevision": 0,
    "replacements": [
      {
        "from": "customer",
        "to": "client"
      }
    ]
  }),
});
const data = await res.json();
console.log(data);
import os

import requests

r = requests.post(
    "https://api.gpt-video.com/v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions/replace",
    headers={
        "Authorization": f"Bearer {os.environ['GPT_VIDEO_API_KEY']}",
    },
    json={
        "expectedRevision": 0,
        "replacements": [
            {
                "from": "customer",
                "to": "client"
            }
        ]
    },
)
data = r.json()
print(data)
{
  "method": "tools/call",
  "params": {
    "name": "replace_caption_words",
    "arguments": {
      "compositionId": "cmp_4e8c2a6f09d14b73a1e5",
      "expectedRevision": 0,
      "replacements": [
        {
          "from": "customer",
          "to": "client"
        }
      ]
    }
  }
}

Response

A successful call answers HTTP 200. Returns a Composition object in composition.

Fields to check

NameTypeDescription
composition.idstringThe composition id. It starts with cmp_.
composition.framing.statusstring
composition.framing.jobIdstring or null

Response example

HTTP 200
{
  "composition": {
    "id": "cmp_4e8c2a6f09d14b73a1e5",
    "projectId": "prj_7c2e9a4f1b3d4e8096a5",
    "clipId": "cnd_9e04a6c3d12b4f78a5b9",
    "kind": "autoclip",
    "title": "Paid before the product",
    "revision": 1,
    "durationSec": 30,
    "width": 1080,
    "height": 1920,
    "fps": 30,
    "source": {
      "assetId": "ast_3f9a1c7e5b2d4086a1c4",
      "startSec": 120,
      "endSec": 150
    },
    "hook": "They paid us before we had a product",
    "captions": {
      "style": "word-pop",
      "wordCount": 75
    },
    "framing": {
      "status": "centered",
      "jobId": null
    },
    "createdAt": "2026-09-01T10:00:00.000Z",
    "updatedAt": "2026-09-01T11:10:00.000Z"
  },
  "replaced": [
    {
      "from": "customer",
      "count": 4
    }
  ],
  "notFound": []
}
All response fields
NameTypeRequiredDescription
compositionComposition objectRequiredDescribed in Composition object.
replacedarray of objectsRequiredEvery replacement that found its word, with how many words it changed.
replaced[].fromstringRequired
replaced[].countintegerRequired
notFoundarray of objectsRequiredEvery replacement that found no word, with up to three similar words of the captions.
notFound[].fromstringRequired
notFound[].similararray of stringsRequired
More examples

A word that is not in the captions

POST /v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions/replace

Request body
{
  "expectedRevision": 0,
  "replacements": [
    {
      "from": "costumer",
      "to": "customer"
    }
  ]
}
HTTP 200
{
  "composition": {
    "id": "cmp_4e8c2a6f09d14b73a1e5",
    "projectId": "prj_7c2e9a4f1b3d4e8096a5",
    "clipId": "cnd_9e04a6c3d12b4f78a5b9",
    "kind": "autoclip",
    "title": "Paid before the product",
    "revision": 0,
    "durationSec": 30,
    "width": 1080,
    "height": 1920,
    "fps": 30,
    "source": {
      "assetId": "ast_3f9a1c7e5b2d4086a1c4",
      "startSec": 120,
      "endSec": 150
    },
    "hook": "They paid us before we had a product",
    "captions": {
      "style": "word-pop",
      "wordCount": 75
    },
    "framing": {
      "status": "centered",
      "jobId": null
    },
    "createdAt": "2026-09-01T10:00:00.000Z",
    "updatedAt": "2026-09-01T10:00:00.000Z"
  },
  "replaced": [],
  "notFound": [
    {
      "from": "costumer",
      "similar": [
        "customer"
      ]
    }
  ]
}

A revision that is not the current one

POST /v1/compositions/cmp_4e8c2a6f09d14b73a1e5/captions/replace

Request body
{
  "expectedRevision": 0,
  "replacements": [
    {
      "from": "customer",
      "to": "client"
    }
  ]
}
HTTP 409
{
  "error": {
    "code": "CONFLICT",
    "message": "This composition changed since revision 0; its revision is now 1.",
    "hint": "Read the composition again with get_composition, then send the change with its current revision.",
    "requestId": "req_5a9c2e7f1b3d48a6c0e4f2b8"
  }
}

A composition of a Vibe Editing project

POST /v1/compositions/cmp_cfbdd85eaaff301fa997/captions/replace

Request body
{
  "expectedRevision": 0,
  "replacements": [
    {
      "from": "launch",
      "to": "Launch"
    }
  ]
}
HTTP 409
{
  "error": {
    "code": "CONFLICT",
    "message": "This composition belongs to a Vibe Editing project. Correct its captions with the replace_caption_word operation of apply_timeline_operations.",
    "requestId": "req_9e4a1c7b3f5d42a8e6c0b2d4"
  }
}

Correct the captions of a composition that does not exist

POST /v1/compositions/cmp_7a1f5d3c9e0b42d8f6a2/captions/replace

Request body
{
  "expectedRevision": 0,
  "replacements": [
    {
      "from": "customer",
      "to": "client"
    }
  ]
}
HTTP 404
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Composition cmp_7a1f5d3c9e0b42d8f6a2 not found",
    "hint": "Check the id. An id that belongs to another account is not found either.",
    "requestId": "req_6b2d9f0e4a7c41b3d8e5a2f9"
  }
}

Errors

CodeStatusWhenWhat to do
NOT_FOUND404Correct the captions of a composition that does not existChange the request, then send it again.
CONFLICT409A revision that is not the current oneRead the resource again, then decide. Do not send the same request again.

Every call can also answer these errors. VALIDATION_FAILED, UNKNOWN_PARAMETER, UNAUTHORIZED, SCOPE_MISSING, IDEMPOTENCY_IN_PROGRESS, PAYLOAD_TOO_LARGE, IDEMPOTENCY_KEY_REUSED, RATE_LIMITED, INTERNAL, UNAVAILABLE.

Cost

No credits are used.

Questions

Can I fix a name that is split into two words?

Each replacement finds one word. Replace the first word with the whole name and the second with a word of its own, or change the video in the GPT-Video app.

What happens to the punctuation?

A word keeps its punctuation: replacing customer with client in «customer,» writes «client,». If the new text ends with punctuation of its own, that one is kept instead.

View as Markdown