MediaCaption API Uploads API

Multipart media upload and AI transcription endpoints.

Operations 6

POST /uploads Preflight a media upload #
GET /uploads/{id} Fetch upload and transcription status #
DELETE /uploads/{id} Cancel an upload before processing #
POST /uploads/{id}/parts Create a signed multipart part URL #
POST /uploads/{id}/complete Complete upload and start transcription #
POST /uploads/{id}/resume Resume transcription after adding credits #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/mediacaption-api-uploads-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

mediacaption-api-uploads-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Media Caption Public Uploads API
  version: 1.0.0
  description: 'Public API for reading account and credit balance details, fetching YouTube

    transcripts, creating bulk transcript jobs, polling jobs, retrieving retained

    transcriptions, and receiving job-level webhooks.'
  contact:
    name: Media Caption
    url: https://mediacaption.io
  license:
    name: Proprietary
    url: https://mediacaption.io/terms
servers:
- url: https://api.mediacaption.io/v1
  description: Production
security:
- bearerApiKey: []
- headerApiKey: []
tags:
- name: Uploads
  description: Multipart media upload and AI transcription endpoints.
paths:
  /uploads:
    post:
      tags:
      - Uploads
      summary: Preflight a media upload
      description: 'Reserves transcription credits before creating any storage upload. The declared

        duration determines the initial reservation; the server measures the uploaded

        media before transcription and reconciles the final cost. Files may be at most

        3 GB (3,000,000,000 bytes).


        End-to-end local-file flow:

        1. Create an upload with `POST /uploads`.

        2. For each numbered file part, request a signed URL from

        `POST /uploads/{id}/parts`, then `PUT` that part directly to the returned

        S3 URL and retain its `ETag` response header.

        3. Submit the ordered part numbers and ETags to

        `POST /uploads/{id}/complete`.

        4. Poll `GET /uploads/{id}`. If it returns `awaiting_credits`, add credits

        and call `POST /uploads/{id}/resume`. When it returns `completed`, fetch

        the returned `transcriptionUrl`.


        The Media Caption API key must not be sent to the signed S3 part URL.'
      operationId: createUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UploadRequest'
            example:
              filename: meeting.mp4
              contentType: video/mp4
              sizeBytes: 16777216
              durationSec: 600
      responses:
        '201':
          description: Credit reservation and multipart upload created
          headers:
            Location:
              schema:
                type: string
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadCreatedResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '413':
          description: File exceeds the 3 GB limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/{id}:
    parameters:
    - $ref: '#/components/parameters/UploadId'
    get:
      tags:
      - Uploads
      summary: Fetch upload and transcription status
      operationId: getUpload
      responses:
        '200':
          description: Upload found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadStatusResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      tags:
      - Uploads
      summary: Cancel an upload before processing
      operationId: cancelUpload
      responses:
        '200':
          description: Upload cancelled and reserved credits refunded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadOperationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/{id}/parts:
    parameters:
    - $ref: '#/components/parameters/UploadId'
    post:
      tags:
      - Uploads
      summary: Create a signed multipart part URL
      description: Request this immediately before uploading the numbered part. The signed URL expires after 15 minutes. Upload the raw bytes with `PUT`, without a Media Caption API key, and retain the S3 `ETag` response header.
      operationId: createUploadPartUrl
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UploadPartUrlRequest'
      responses:
        '200':
          description: Signed part URL created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadPartUrlResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/{id}/complete:
    parameters:
    - $ref: '#/components/parameters/UploadId'
    post:
      tags:
      - Uploads
      summary: Complete upload and start transcription
      description: Submit every part number and S3 ETag in ascending order. The API completes the S3 multipart upload, verifies the actual object size, and queues ElevenLabs Scribe transcription.
      operationId: completeUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UploadCompleteRequest'
      responses:
        '202':
          description: Upload accepted for transcription
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadOperationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          description: Uploaded object does not match the declared size or exceeds 3 GB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /uploads/{id}/resume:
    parameters:
    - $ref: '#/components/parameters/UploadId'
    post:
      tags:
      - Uploads
      summary: Resume transcription after adding credits
      description: Rechecks the additional credits required after server-side duration measurement, then retries processing.
      operationId: resumeUpload
      responses:
        '200':
          description: Upload was already completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadOperationResponse'
        '202':
          description: Upload transcription resumed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadOperationResponse'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  responses:
    InsufficientCredits:
      description: Not enough credits to start processing
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: insufficient_credits
              message: Insufficient credits.
    InvalidRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missing:
              value:
                error:
                  code: missing_api_key
                  message: Missing API key.
            invalid:
              value:
                error:
                  code: invalid_api_key
                  message: Invalid API key.
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    UploadCompleteRequest:
      type: object
      additionalProperties: false
      required:
      - parts
      properties:
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            type: object
            additionalProperties: false
            required:
            - partNumber
            - etag
            properties:
              partNumber:
                type: integer
                minimum: 1
                maximum: 10000
              etag:
                type: string
    UploadCreatedResponse:
      type: object
      additionalProperties: false
      required:
      - id
      - status
      - requiredCredits
      - minPartSizeBytes
      - partUrl
      - completeUrl
      - statusUrl
      - expiresAt
      properties:
        id:
          type: string
        status:
          type: string
          enum:
          - uploading
        requiredCredits:
          type: integer
        minPartSizeBytes:
          type: integer
        partUrl:
          type: string
        completeUrl:
          type: string
        statusUrl:
          type: string
        expiresAt:
          type: string
          format: date-time
    UploadRequest:
      type: object
      additionalProperties: false
      required:
      - filename
      - contentType
      - sizeBytes
      - durationSec
      properties:
        filename:
          type: string
          maxLength: 400
          example: meeting.mp4
        contentType:
          type: string
          description: An audio/* or video/* media type.
          example: video/mp4
        sizeBytes:
          type: integer
          minimum: 1
          maximum: 3000000000
        durationSec:
          type: integer
          minimum: 1
          description: Client-measured duration used for the pre-upload credit reservation.
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
      - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
          - code
          - message
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
    UploadStatus:
      type: string
      enum:
      - initiated
      - uploading
      - uploaded
      - processing
      - awaiting_credits
      - completed
      - failed
      - aborted
    ErrorCode:
      type: string
      enum:
      - concurrent_job_limit_exceeded
      - geo_restricted
      - invalid_api_key
      - invalid_request
      - insufficient_credits
      - internal_error
      - missing_api_key
      - not_found
      - public_api_rate_limited
      - single_transcript_rate_limited
      - transcription_expired
      - transcript_unavailable
      - video_unavailable
      - webhook_not_found
      - youtube_blocked
      - youtube_rate_limited
    UploadPartUrlResponse:
      type: object
      additionalProperties: false
      required:
      - partNumber
      - uploadUrl
      - expiresInSeconds
      properties:
        partNumber:
          type: integer
        uploadUrl:
          type: string
          format: uri
        expiresInSeconds:
          type: integer
    UploadPartUrlRequest:
      type: object
      additionalProperties: false
      required:
      - partNumber
      properties:
        partNumber:
          type: integer
          minimum: 1
          maximum: 10000
    UploadOperationResponse:
      type: object
      additionalProperties: true
      required:
      - id
      - status
      properties:
        id:
          type: string
        status:
          $ref: '#/components/schemas/UploadStatus'
        requiredCredits:
          type: integer
        additionalCredits:
          type: integer
    UploadStatusResponse:
      type: object
      additionalProperties: false
      required:
      - id
      - filename
      - sizeBytes
      - durationSec
      - requiredCredits
      - reservedCredits
      - status
      - progress
      - stage
      - error
      - createdAt
      - completedAt
      properties:
        id:
          type: string
        filename:
          type:
          - string
          - 'null'
        sizeBytes:
          type:
          - integer
          - 'null'
        durationSec:
          type:
          - integer
          - 'null'
        requiredCredits:
          type:
          - integer
          - 'null'
        reservedCredits:
          type:
          - integer
          - 'null'
        status:
          $ref: '#/components/schemas/UploadStatus'
        progress:
          type:
          - integer
          - 'null'
        stage:
          type:
          - string
          - 'null'
        error:
          type:
          - string
          - 'null'
        createdAt:
          type: string
          format: date-time
        completedAt:
          type:
          - string
          - 'null'
          format: date-time
        transcriptionId:
          type: string
        transcriptionUrl:
          type: string
  headers:
    XRequestId:
      description: Request ID for troubleshooting.
      schema:
        type: string
        example: req_01jz7mb36gp7h2nm5rwd1ah4zz
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        example: 30
  parameters:
    UploadId:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: ^upl_[0-9a-fA-F-]{36}$
      example: upl_3f6e5c6a-0d31-4f8c-9b1d-7f3b9e6a2c11
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: Media Caption API key
      description: 'Use `Authorization: Bearer mc_live_xxx`.'
    headerApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Alternative API key header.