Get a video transcript

GET /v1/transcripts/{video_id}

Caption reads use 4 credits per started 15 minutes. quality=premium is one read. An owned transcript answers 200 ready at no charge. Otherwise this call starts a Premium transcript of the whole video, 300 credits per started 15 minutes, using credits from the account's plan first and then the account's on-demand budget up to its limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same job and never charge twice. When the last Premium transcript for the video failed, the read answers 200 state failed with the job and last_attempt and charges nothing; retry=true starts a new one. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and the limit it met in limit, and nothing is charged. A default-premium account with nothing owned reads captions with a note. On captions, start and end set the billed window: 4 credits per started 15 minutes of end minus start. On Premium they only trim the returned content, and the charge is the whole video. language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. language picks the language: language= returns the base transcript (base_language), and a language the transcript is not in returns it translated, line for line with the same timestamps and speakers. Captions offer only the languages YouTube makes available for the video (translation_languages), billed as a caption read; any other language is Premium: an owned Premium transcript in another language costs the Premium rate once per account and language.

Parameters

NameLocationDescription
video_id (required)pathYouTube video id, 11 characters.
qualityquerycaptions reads creator or automatic captions at 4 credits per started 15 minutes. premium is one read. An owned transcript returns 200 state ready at no charge. Otherwise the read starts a Premium transcript of the whole video, 300 credits per started 15 minutes, using credits from the account's plan first and then the account's on-demand budget up to its limit, and returns 202 state pending with the job until it is ready. When the last Premium transcript for the video failed it answers 200 state failed and starts a new one only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.
languagequeryThe language to read the transcript in, as a code (e.g. "es") or a comma-separated priority list of at most 5 (e.g. "de,en"); asr picks the first automatic caption track and asr-<code> a specific one. Every transcript has a base language, base_language in the response: the language of the original caption track, or the language a Premium transcript was spoken and transcribed in. language= with no value returns the base transcript; so does a code in the base language. Omitted, the account default applies (en unless changed), falling back to the base transcript. When no code in the list matches, the transcript is translated into the first code, line for line with the same timestamps and speakers. Captions translate only into the languages YouTube makes available for the video (translation_languages in the response, or GET /v1/videos/{video_id}/captions), billed as a caption read of the window, 4 credits per started 15 minutes; any other code answers 400 invalid_query naming the Premium read. On a Premium transcript the account owns, each other language costs the Premium rate, 300 credits per started 15 minutes, once per account and language, and is free after that. While a translation runs the read answers 503 transcript_fetching with Retry-After and progress, and charges nothing. languages[] lists every caption track the video offers.
timestampsqueryfalse returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true.
startqueryWindow start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims the returned content. An explicit Premium read is charged for the whole video from the account's plan credits and then its on-demand budget; a window does not reduce that charge.
endqueryWindow end in seconds, greater than start and no greater than the video duration. Send start and end together.
retryqueryPremium only; captions with retry=true returns invalid_query. When the last Premium transcript for this video failed, a read answers 200 state failed with the job and last_attempt and charges nothing; retry=true starts a new one under the same quote, budget and one-job-per-video rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed job it changes nothing.
refreshqueryCaptions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query.
srcqueryThe surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.

Responses

200 state ready: the transcript (video, quality, source, language, base_language, languages, translation_languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last Premium transcript for this video failed; job and last_attempt say why, nothing was charged, and retry=true starts a new one.
{
  "description": "state ready: the transcript (video, quality, source, language, base_language, languages, translation_languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last Premium transcript for this video failed; job and last_attempt say why, nothing was charged, and retry=true starts a new one.",
  "headers": {
    "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
    },
    "X-Arcmira-Version": {
      "$ref": "#/components/headers/X-Arcmira-Version"
    },
    "RateLimit-Limit": {
      "$ref": "#/components/headers/RateLimit-Limit"
    },
    "RateLimit-Remaining": {
      "$ref": "#/components/headers/RateLimit-Remaining"
    },
    "RateLimit-Reset": {
      "$ref": "#/components/headers/RateLimit-Reset"
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/TranscriptResponse"
          },
          {
            "$ref": "#/components/schemas/TranscriptFailed"
          }
        ],
        "discriminator": {
          "propertyName": "state",
          "mapping": {
            "ready": "#/components/schemas/TranscriptResponse",
            "failed": "#/components/schemas/TranscriptFailed"
          }
        }
      }
    }
  }
}
202 state pending: the Premium transcript this read started or joined is in flight; job carries its status and poll interval. No transcript content yet.
{
  "description": "state pending: the Premium transcript this read started or joined is in flight; job carries its status and poll interval. No transcript content yet.",
  "headers": {
    "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
    },
    "X-Arcmira-Version": {
      "$ref": "#/components/headers/X-Arcmira-Version"
    },
    "RateLimit-Limit": {
      "$ref": "#/components/headers/RateLimit-Limit"
    },
    "RateLimit-Remaining": {
      "$ref": "#/components/headers/RateLimit-Remaining"
    },
    "RateLimit-Reset": {
      "$ref": "#/components/headers/RateLimit-Reset"
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/TranscriptPending"
      }
    }
  }
}
400 InvalidRequest
{
  "$ref": "#/components/responses/InvalidRequest"
}
401 AuthenticationError
{
  "$ref": "#/components/responses/AuthenticationError"
}
402 quota_exceeded, spend_limit_exceeded, member_limit, on_demand_paused or fair_use_cap. The plan includes Premium but the credits left on the plan and the on-demand budget do not cover this video; quote carries the refused price, limit carries the limit it met when the server holds one, and nothing was charged.
{
  "description": "quota_exceeded, spend_limit_exceeded, member_limit, on_demand_paused or fair_use_cap. The plan includes Premium but the credits left on the plan and the on-demand budget do not cover this video; quote carries the refused price, limit carries the limit it met when the server holds one, and nothing was charged.",
  "headers": {
    "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
    },
    "X-Arcmira-Version": {
      "$ref": "#/components/headers/X-Arcmira-Version"
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/Error"
      }
    }
  }
}
403 paid_plan_required. error.unlock names the plan that includes Premium; quote carries the price.
{
  "description": "paid_plan_required. error.unlock names the plan that includes Premium; quote carries the price.",
  "headers": {
    "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
    },
    "X-Arcmira-Version": {
      "$ref": "#/components/headers/X-Arcmira-Version"
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/Error"
      }
    }
  }
}
404 NotFound
{
  "$ref": "#/components/responses/NotFound"
}
429 RateLimited
{
  "$ref": "#/components/responses/RateLimited"
}
500 ServerError
{
  "$ref": "#/components/responses/ServerError"
}
503 transcript_fetching. The caption track is being fetched, or the transcript translated, now. Retry-After and retry_after_seconds carry the wait; a translation also carries progress (done and total lines). Nothing was charged by this call.
{
  "description": "transcript_fetching. The caption track is being fetched, or the transcript translated, now. Retry-After and retry_after_seconds carry the wait; a translation also carries progress (done and total lines). Nothing was charged by this call.",
  "headers": {
    "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
    },
    "X-Arcmira-Version": {
      "$ref": "#/components/headers/X-Arcmira-Version"
    },
    "Retry-After": {
      "$ref": "#/components/headers/Retry-After"
    }
  },
  "content": {
    "application/json": {
      "schema": {
        "$ref": "#/components/schemas/Error"
      }
    }
  }
}

Operation definition

{
  "tags": [
    "Transcripts"
  ],
  "operationId": "get_transcript",
  "summary": "Get a video transcript",
  "description": "Caption reads use 4 credits per started 15 minutes. quality=premium is one read. An owned transcript answers 200 ready at no charge. Otherwise this call starts a Premium transcript of the whole video, 300 credits per started 15 minutes, using credits from the account's plan first and then the account's on-demand budget up to its limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same job and never charge twice. When the last Premium transcript for the video failed, the read answers 200 state failed with the job and last_attempt and charges nothing; retry=true starts a new one. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and the limit it met in limit, and nothing is charged. A default-premium account with nothing owned reads captions with a note. On captions, start and end set the billed window: 4 credits per started 15 minutes of end minus start. On Premium they only trim the returned content, and the charge is the whole video. language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. language picks the language: language= returns the base transcript (base_language), and a language the transcript is not in returns it translated, line for line with the same timestamps and speakers. Captions offer only the languages YouTube makes available for the video (translation_languages), billed as a caption read; any other language is Premium: an owned Premium transcript in another language costs the Premium rate once per account and language.",
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "parameters": [
    {
      "schema": {
        "type": "string",
        "description": "YouTube video id, 11 characters."
      },
      "required": true,
      "description": "YouTube video id, 11 characters.",
      "name": "video_id",
      "in": "path"
    },
    {
      "schema": {
        "type": "string",
        "enum": [
          "captions",
          "premium"
        ],
        "description": "captions reads creator or automatic captions at 4 credits per started 15 minutes. premium is one read. An owned transcript returns 200 state ready at no charge. Otherwise the read starts a Premium transcript of the whole video, 300 credits per started 15 minutes, using credits from the account's plan first and then the account's on-demand budget up to its limit, and returns 202 state pending with the job until it is ready. When the last Premium transcript for the video failed it answers 200 state failed and starts a new one only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings."
      },
      "required": false,
      "description": "captions reads creator or automatic captions at 4 credits per started 15 minutes. premium is one read. An owned transcript returns 200 state ready at no charge. Otherwise the read starts a Premium transcript of the whole video, 300 credits per started 15 minutes, using credits from the account's plan first and then the account's on-demand budget up to its limit, and returns 202 state pending with the job until it is ready. When the last Premium transcript for the video failed it answers 200 state failed and starts a new one only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.",
      "name": "quality",
      "in": "query"
    },
    {
      "schema": {
        "type": "string",
        "description": "The language to read the transcript in, as a code (e.g. \"es\") or a comma-separated priority list of at most 5 (e.g. \"de,en\"); asr picks the first automatic caption track and asr-<code> a specific one. Every transcript has a base language, base_language in the response: the language of the original caption track, or the language a Premium transcript was spoken and transcribed in. language= with no value returns the base transcript; so does a code in the base language. Omitted, the account default applies (en unless changed), falling back to the base transcript. When no code in the list matches, the transcript is translated into the first code, line for line with the same timestamps and speakers. Captions translate only into the languages YouTube makes available for the video (translation_languages in the response, or GET /v1/videos/{video_id}/captions), billed as a caption read of the window, 4 credits per started 15 minutes; any other code answers 400 invalid_query naming the Premium read. On a Premium transcript the account owns, each other language costs the Premium rate, 300 credits per started 15 minutes, once per account and language, and is free after that. While a translation runs the read answers 503 transcript_fetching with Retry-After and progress, and charges nothing. languages[] lists every caption track the video offers."
      },
      "required": false,
      "description": "The language to read the transcript in, as a code (e.g. \"es\") or a comma-separated priority list of at most 5 (e.g. \"de,en\"); asr picks the first automatic caption track and asr-<code> a specific one. Every transcript has a base language, base_language in the response: the language of the original caption track, or the language a Premium transcript was spoken and transcribed in. language= with no value returns the base transcript; so does a code in the base language. Omitted, the account default applies (en unless changed), falling back to the base transcript. When no code in the list matches, the transcript is translated into the first code, line for line with the same timestamps and speakers. Captions translate only into the languages YouTube makes available for the video (translation_languages in the response, or GET /v1/videos/{video_id}/captions), billed as a caption read of the window, 4 credits per started 15 minutes; any other code answers 400 invalid_query naming the Premium read. On a Premium transcript the account owns, each other language costs the Premium rate, 300 credits per started 15 minutes, once per account and language, and is free after that. While a translation runs the read answers 503 transcript_fetching with Retry-After and progress, and charges nothing. languages[] lists every caption track the video offers.",
      "name": "language",
      "in": "query"
    },
    {
      "schema": {
        "type": "boolean",
        "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true."
      },
      "required": false,
      "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true.",
      "name": "timestamps",
      "in": "query"
    },
    {
      "schema": {
        "type": [
          "number",
          "null"
        ],
        "minimum": 0,
        "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims the returned content. An explicit Premium read is charged for the whole video from the account's plan credits and then its on-demand budget; a window does not reduce that charge."
      },
      "required": false,
      "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims the returned content. An explicit Premium read is charged for the whole video from the account's plan credits and then its on-demand budget; a window does not reduce that charge.",
      "name": "start",
      "in": "query"
    },
    {
      "schema": {
        "type": [
          "number",
          "null"
        ],
        "minimum": 0,
        "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together."
      },
      "required": false,
      "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together.",
      "name": "end",
      "in": "query"
    },
    {
      "schema": {
        "type": "boolean",
        "description": "Premium only; captions with retry=true returns invalid_query. When the last Premium transcript for this video failed, a read answers 200 state failed with the job and last_attempt and charges nothing; retry=true starts a new one under the same quote, budget and one-job-per-video rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed job it changes nothing."
      },
      "required": false,
      "description": "Premium only; captions with retry=true returns invalid_query. When the last Premium transcript for this video failed, a read answers 200 state failed with the job and last_attempt and charges nothing; retry=true starts a new one under the same quote, budget and one-job-per-video rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed job it changes nothing.",
      "name": "retry",
      "in": "query"
    },
    {
      "schema": {
        "type": "boolean",
        "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query."
      },
      "required": false,
      "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query.",
      "name": "refresh",
      "in": "query"
    },
    {
      "schema": {
        "type": "string",
        "enum": [
          "mcp-tool"
        ],
        "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client."
      },
      "required": false,
      "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.",
      "name": "src",
      "in": "query"
    }
  ],
  "responses": {
    "200": {
      "description": "state ready: the transcript (video, quality, source, language, base_language, languages, translation_languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last Premium transcript for this video failed; job and last_attempt say why, nothing was charged, and retry=true starts a new one.",
      "headers": {
        "X-Request-Id": {
          "$ref": "#/components/headers/X-Request-Id"
        },
        "X-Arcmira-Version": {
          "$ref": "#/components/headers/X-Arcmira-Version"
        },
        "RateLimit-Limit": {
          "$ref": "#/components/headers/RateLimit-Limit"
        },
        "RateLimit-Remaining": {
          "$ref": "#/components/headers/RateLimit-Remaining"
        },
        "RateLimit-Reset": {
          "$ref": "#/components/headers/RateLimit-Reset"
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/TranscriptResponse"
              },
              {
                "$ref": "#/components/schemas/TranscriptFailed"
              }
            ],
            "discriminator": {
              "propertyName": "state",
              "mapping": {
                "ready": "#/components/schemas/TranscriptResponse",
                "failed": "#/components/schemas/TranscriptFailed"
              }
            }
          }
        }
      }
    },
    "202": {
      "description": "state pending: the Premium transcript this read started or joined is in flight; job carries its status and poll interval. No transcript content yet.",
      "headers": {
        "X-Request-Id": {
          "$ref": "#/components/headers/X-Request-Id"
        },
        "X-Arcmira-Version": {
          "$ref": "#/components/headers/X-Arcmira-Version"
        },
        "RateLimit-Limit": {
          "$ref": "#/components/headers/RateLimit-Limit"
        },
        "RateLimit-Remaining": {
          "$ref": "#/components/headers/RateLimit-Remaining"
        },
        "RateLimit-Reset": {
          "$ref": "#/components/headers/RateLimit-Reset"
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TranscriptPending"
          }
        }
      }
    },
    "400": {
      "$ref": "#/components/responses/InvalidRequest"
    },
    "401": {
      "$ref": "#/components/responses/AuthenticationError"
    },
    "402": {
      "description": "quota_exceeded, spend_limit_exceeded, member_limit, on_demand_paused or fair_use_cap. The plan includes Premium but the credits left on the plan and the on-demand budget do not cover this video; quote carries the refused price, limit carries the limit it met when the server holds one, and nothing was charged.",
      "headers": {
        "X-Request-Id": {
          "$ref": "#/components/headers/X-Request-Id"
        },
        "X-Arcmira-Version": {
          "$ref": "#/components/headers/X-Arcmira-Version"
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "paid_plan_required. error.unlock names the plan that includes Premium; quote carries the price.",
      "headers": {
        "X-Request-Id": {
          "$ref": "#/components/headers/X-Request-Id"
        },
        "X-Arcmira-Version": {
          "$ref": "#/components/headers/X-Arcmira-Version"
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "404": {
      "$ref": "#/components/responses/NotFound"
    },
    "429": {
      "$ref": "#/components/responses/RateLimited"
    },
    "500": {
      "$ref": "#/components/responses/ServerError"
    },
    "503": {
      "description": "transcript_fetching. The caption track is being fetched, or the transcript translated, now. Retry-After and retry_after_seconds carry the wait; a translation also carries progress (done and total lines). Nothing was charged by this call.",
      "headers": {
        "X-Request-Id": {
          "$ref": "#/components/headers/X-Request-Id"
        },
        "X-Arcmira-Version": {
          "$ref": "#/components/headers/X-Arcmira-Version"
        },
        "Retry-After": {
          "$ref": "#/components/headers/Retry-After"
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}