MediaCaption API Uploads API
Multipart media upload and AI transcription endpoints.
Multipart media upload and AI transcription endpoints.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/mediacaption-api-uploads-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
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: 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.