> ## Documentation Index
> Fetch the complete documentation index at: https://docs.captioncraft.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Create subtitles

> Create a captioned video and reserve prepaid seconds.

Requires `subtitles:write`. Submit a direct HTTPS video URL and poll the returned `status_url` every five seconds.

Choose any of the 16 presets and optionally override typography, layout, background, outline, shadow, and applicable highlight colors through `style`. Omitted fields inherit the preset, including fields within nested objects. [Caption styles](/caption-styles) explains all controls and preset-specific behavior.

<Note>Creating a job reserves `max_duration_seconds`. This limit does not trim the video. Successful jobs are billed for actual duration rounded up; failed and canceled jobs release the reservation.</Note>

See [Quickstart](/quickstart) for a full submission and download example, and [Media requirements](/media-requirements) for accepted sources.


## OpenAPI

````yaml openapi.json POST /v1/subtitles
openapi: 3.1.0
info:
  title: CaptionCraft API
  version: 1.0.0
  description: >-
    Create captioned MP4 videos from a video URL. Jobs are asynchronous. Poll
    every 5 seconds. Media is retained for 24 hours; request a fresh job
    response to retrieve result URLs. All word timestamps use seconds.
servers:
  - url: https://api.captioncraft.studio
security: []
paths:
  /v1/subtitles:
    post:
      summary: Create a captioned video
      description: >-
        Reserves max_duration_seconds (default 60, maximum 600) from prepaid
        credit. Once probed, excess is released. Charges ceil(actual duration)
        exactly once on success; failures and cancellations release the
        reservation. Direct HTTPS MP4/MOV/WebM URLs only; redirects are not
        followed. Maximum 500 MiB and 1080p in either orientation.
      operationId: createSubtitles
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 128
            pattern: ^[A-Za-z0-9._:-]{8,128}$
          description: >-
            Reuse with the same input to retry safely. Different input with the
            same key returns 409.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubtitleRequest'
      responses:
        '202':
          description: Accepted; poll status_url every five seconds
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - status
                  - status_url
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  status_url:
                    type: string
                    format: uri
                  replayed:
                    type: boolean
              example:
                id: example_job_id
                status: ingesting
                status_url: https://api.captioncraft.studio/v1/jobs/example_job_id
                replayed: false
          headers:
            Location:
              description: Status URL for the accepted job.
              schema:
                type: string
                format: uri
            Retry-After:
              description: Recommended polling interval in seconds.
              schema:
                type: integer
                example: 5
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: Insufficient prepaid seconds
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Missing key scope or disabled account
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Idempotency key reused with different input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: JSON body exceeds 16 KiB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          description: 'Use Content-Type: application/json'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate or concurrency limit exceeded; honor Retry-After
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            Retry-After:
              description: Minimum retry delay in seconds.
              schema:
                type: integer
                example: 5
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: Processing is not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - apiKey: []
components:
  schemas:
    SubtitleRequest:
      type: object
      properties:
        video_url:
          type: string
          maxLength: 4096
          format: uri
          description: >-
            Direct HTTPS MP4, MOV, or WebM URL. No redirects or extra auth
            headers. Maximum 500 MiB; must remain valid until downloaded.
          examples:
            - https://your-cdn.example/video.mp4
        preset:
          default: classic
          type: string
          enum:
            - classic
            - highlight
            - smash
            - box
            - push
            - elegant
            - ogre
            - outline
            - paper-ink
            - action
            - prompter
            - storytelling
            - karaoke
            - glow
            - thin&bold
            - background
          description: Caption preset. Omitted style properties use this preset's defaults.
        language:
          type: string
          pattern: ^[a-z]{2,3}$
          description: >-
            Optional lowercase two- or three-letter language code supported by
            transcription. Omit for automatic language detection. Does not
            translate.
          examples:
            - en
        max_duration_seconds:
          default: 60
          type: integer
          minimum: 1
          maximum: 600
          description: >-
            Hard duration limit and initial prepaid reservation. Does not trim
            the video. Actual duration must fit this limit.
        style:
          default: {}
          type: object
          properties:
            position:
              description: >-
                Vertical caption placement; omitted values use the preset's
                default.
              type: string
              enum:
                - top
                - center
                - bottom
            horizontal_position:
              description: Horizontal placement of the caption block.
              type: string
              enum:
                - left
                - center
                - right
            text_color:
              description: Text color as a six-digit hexadecimal color.
              type: string
              pattern: ^#[0-9a-fA-F]{6}$
            highlight_color:
              description: >-
                Active-word color for highlight, push, action and karaoke;
                active-word box color for box. Ignored by other presets for
                compatibility.
              type: string
              pattern: ^#[0-9a-fA-F]{6}$
            font_family:
              description: A bundled font; custom font URLs are not accepted.
              type: string
              enum:
                - Inter
                - Urbanist
                - Rubik
                - Bangers
                - Paytone One
                - Slackey
                - Permanent Marker
                - Caveat Brush
                - Zilla Slab
                - Young Serif
                - Instrument Serif
                - Bebas Neue
                - Coiny
            font_size:
              description: >-
                Font size in pixels on the 1080px caption canvas, before scale.
                Omit to derive from font_family and text_width.
              type: number
              minimum: 12
              maximum: 240
            font_weight:
              description: Regular (400) or bold (700). thin&bold animates its own weight.
              anyOf:
                - type: number
                  const: 400
                - type: number
                  const: 700
            font_style:
              type: string
              enum:
                - normal
                - italic
            text_alignment:
              description: Alignment of lines within the caption block.
              type: string
              enum:
                - left
                - center
                - right
            text_case:
              description: >-
                Visual casing only; downloaded transcripts preserve the original
                words.
              type: string
              enum:
                - none
                - uppercase
                - lowercase
                - capitalize
            text_width:
              description: >-
                Text density: small uses larger type and fewer words per line.
                Defaults to the preset's width for portrait/square, large for
                landscape. Ignored when font_size is explicit.
              type: string
              enum:
                - small
                - medium
                - large
            line_count:
              description: >-
                Maximum lines per cue. 0 displays one word at a time; 100 allows
                up to 100 lines. Smash defaults to 0.
              anyOf:
                - type: number
                  const: 0
                - type: number
                  const: 1
                - type: number
                  const: 2
                - type: number
                  const: 3
                - type: number
                  const: 100
            scale:
              description: >-
                Caption scale multiplied by video width / 1080. Defaults to the
                preset's scale for portrait/square, 0.55 for landscape.
              type: number
              minimum: 0.2
              maximum: 1
            offset:
              description: >-
                Pixel offsets in output-video coordinates. Left minus right
                moves horizontally; top minus bottom vertically. Unspecified
                top/bottom inherit the preset plus an inset on the selected
                edge: 20% of height for portrait/square, 14% for landscape.
                Explicit values replace the resolved offset, including zero.
              type: object
              properties:
                left:
                  type: number
                  minimum: -1920
                  maximum: 1920
                top:
                  type: number
                  minimum: -1920
                  maximum: 1920
                right:
                  type: number
                  minimum: -1920
                  maximum: 1920
                bottom:
                  type: number
                  minimum: -1920
                  maximum: 1920
              additionalProperties: false
            background:
              description: >-
                Caption background. Set type to enable it; color and opacity
                alone preserve the preset's background type.
              type: object
              properties:
                type:
                  type: string
                  enum:
                    - none
                    - line
                    - line-rounded
                    - box
                    - box-rounded
                color:
                  type: string
                  pattern: ^#[0-9a-fA-F]{6}$
                opacity:
                  type: number
                  minimum: 0
                  maximum: 1
              additionalProperties: false
            outline:
              description: Text outline on the caption canvas. Thickness 0 disables it.
              type: object
              properties:
                color:
                  type: string
                  pattern: ^#[0-9a-fA-F]{6}$
                thickness:
                  type: number
                  minimum: 0
                  maximum: 20
              additionalProperties: false
            shadow:
              description: >-
                Text shadow. Set enabled to show it. Distance and angle
                determine its position: 0 degrees points down, 90 points right.
              type: object
              properties:
                enabled:
                  type: boolean
                color:
                  type: string
                  pattern: ^#[0-9a-fA-F]{6}$
                blur:
                  type: number
                  minimum: 0
                  maximum: 40
                distance:
                  type: number
                  minimum: 0
                  maximum: 20
                angle:
                  type: number
                  minimum: -180
                  maximum: 180
              additionalProperties: false
          additionalProperties: false
          description: >-
            Optional caption style overrides. Omitted fields (including nested
            fields) inherit the selected preset. GET /v1/presets lists defaults,
            supported controls and preset-specific behavior.
      required:
        - video_url
      additionalProperties: false
      examples:
        - video_url: https://your-cdn.example/video.mp4
          preset: highlight
          max_duration_seconds: 60
          style:
            highlight_color: '#7651E8'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            request_id:
              type: string
          required:
            - code
            - message
            - request_id
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: cc_ API key

````