UniScribe Logo

UniScribe OpenAPI Documentation (Beta)#

⚠️ Beta Version Notice This API is currently in beta. Features and endpoints may change without notice. We recommend testing thoroughly in development environments before production use. Please report any issues or feedback to our support team.

Overview#

The UniScribe OpenAPI provides programmatic access to our transcription services, enabling you to integrate audio and video transcription into your workflows, applications, and automation tools like n8n, Zapier, and Make.

Base URL#

https://api.uniscribe.co

Authentication#

API Key Authentication#

Include your API key in the request header:

X-API-Key: your_api_key_here

Getting Your API Key#

  1. Log in to your UniScribe account
  2. Navigate to Settings → API Keys
  3. Click "Create New Key"
  4. Copy and securely store your API key

Important:

  • API access requires an active subscription or LTD plan
  • Free users and one-time purchase users cannot access the API
  • Keep your API key secure and never share it publicly

MCP Server#

Besides the REST API, UniScribe exposes a read-only MCP (Model Context Protocol) server at https://mcp.uniscribe.co/mcp, so AI assistants like Claude Code, Claude Desktop, and Codex CLI can search and cite your transcripts directly. It uses the same API keys described above, sent as Authorization: Bearer <API key>. See the MCP server guide and the Claude integration guide for setup.

File Upload Workflow#

The OpenAPI supports two ways to provide files for transcription:

Option 1: Upload Files Directly#

  1. Get Upload URLs: Call /api/v1/files/upload-url to get pre-signed URLs
  2. Upload File: Use the upload URL to upload your file directly to our storage
  3. Create Transcription: Use the returned file_key to create a transcription

Option 2: External File URLs#

Provide a publicly accessible file URL for transcription.

Requirements for both options:

  • File must be in a supported format
  • File size must not exceed 5GB
  • File duration must not exceed 10 hours

Additional requirements for external URLs:

  • File must be publicly accessible via HTTP/HTTPS and downloadable.
  • YouTube URLs are not supported (use the dedicated YouTube endpoint instead)

Endpoints#

1. Get File Upload URLs#

Endpoint: POST /api/v1/files/upload-url

Description: Generate pre-signed URLs for uploading files directly to our storage

Request:

POST /api/v1/files/upload-url Content-Type: application/json X-API-Key: your_api_key { "filename": "my-audio.mp3", "file_size": 1048576, "upload_expires_in": 3600, "download_expires_in": 1800 }

Parameters:

  • filename (string, required): Name of the file to upload
  • file_size (integer, required): Size of the file in bytes
  • upload_expires_in (integer, optional): Upload URL expiration time in seconds (default: 3600, max: 86400)
  • download_expires_in (integer, optional): Download URL expiration time in seconds (default: 1800, max: 7200)

Response:

{ "success": true, "data": { "upload_url": "https://r2.cloudflare.com/bucket/12345-abc123.mp3?X-Amz-Signature=...", "download_url": "https://r2.cloudflare.com/bucket/12345-abc123.mp3?X-Amz-Signature=...", "file_key": "12345-abc123def456.mp3", "upload_expires_at": "2024-01-16T11:30:00Z", "download_expires_at": "2024-01-16T10:30:00Z" }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

Usage:

  1. Call this endpoint to get upload URLs
  2. Use the upload_url to upload your file (PUT request with file content)
  3. Use the file_key to create a transcription task

2. Create Transcription#

Endpoint: POST /api/v1/transcriptions

Description: Create a new transcription task from an uploaded file or external URL

Request Options:

Option 1: Using uploaded file

POST /api/v1/transcriptions Content-Type: application/json X-API-Key: your_api_key { "file_key": "12345-abc123def456.mp3", "filename": "my_audio_file.mp3", "language_code": "en", "transcription_type": "transcript", "enable_speaker_diarization": false, "webhook_url": "https://your-webhook-url.com" }

Option 2: Using external file URL

POST /api/v1/transcriptions Content-Type: application/json X-API-Key: your_api_key { "file_url": "https://your-storage.com/audio.mp3", "filename": "my-audio-file.mp3", "language_code": "en", "transcription_type": "transcript", "enable_speaker_diarization": false, "webhook_url": "https://your-webhook-url.com" }

Parameters:

  • file_key (string, required if file_url not provided): File key from upload API
  • file_url (string, required if file_key not provided): Public URL to audio/video file (Note: YouTube URLs are not supported here, use /api/v1/transcriptions/youtube instead)
  • filename (string, optional, but recommended): Custom filename for the transcription. If not provided:
    • For uploaded files: filename will be extracted from the file_key
    • For URL downloads: filename will be extracted from the URL path
  • language_code (string, optional): Language code (e.g., "en", "zh", "es"), see Language Support. Omit it, or pass "auto", to detect the language automatically
  • transcription_type (string, optional): "transcript" or "subtitle"
  • enable_speaker_diarization (boolean, optional): Enable speaker identification
  • webhook_url (string, optional): URL to receive completion notifications

Note: You must provide either file_key OR file_url, but not both.

Supported File Types:

  • Audio: mp3, mpeg, mpga, m4a, wav, aac, ogg, opus, flac
  • Video: mp4, webm, mov (audio will be extracted)

Response:

{ "success": true, "data": { "id": "1234567890", "status": "queued", "created_at": "2024-01-15T10:30:00Z" } }

Status Values:

  • queued: Task is queued for processing
  • preprocessing: Media file is being preprocessed (for large files or youtube videos)
  • processing: Transcription is in progress
  • completed: Transcription is complete
  • failed: Transcription failed

3. List Transcriptions#

Retrieve a paginated list of your transcriptions.

Endpoint: GET /api/v1/transcriptions

Parameters:

  • cursor (integer, optional): Pagination cursor
  • limit (integer, optional): Number of items per page (1-20, default: 10)

Example:

GET /api/v1/transcriptions?limit=10&cursor=1234567890 X-API-Key: your_api_key

Response:

{ "success": true, "data": { "items": [ { "id": "1234567890", "filename": "audio.mp3", "status": "completed", "duration": 120.5, "language_code": "en", "transcription_type": "transcript", "created_at": "2024-01-15T10:30:00Z", "completed_at": "2024-01-15T10:35:00Z", "file_size": 1048576, "source_type": "upload" } ], "has_more": true, "next_cursor": "1234567889" }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

4. Get Transcription Details#

Retrieve detailed information and results for a specific transcription.

Endpoint: GET /api/v1/transcriptions/{id}

Example:

GET /api/v1/transcriptions/1234567890 X-API-Key: your_api_key

Response:

{ "success": true, "data": { "id": "1234567890", "filename": "audio.mp3", "status": "completed", "duration": 120.5, "language_code": "en", "transcription_type": "transcript", "created_at": "2024-01-15T10:30:00Z", "completed_at": "2024-01-15T10:35:00Z", "file_size": 1048576, "source_type": "upload", "source_url": "https://your-storage.com/audio.mp3", "result": { "text": "Hello, this is a sample transcription...", "summary": "This audio discusses...", "outline": "# 1. Introduction\n2. Main Points\n3. Conclusion", // in markdown format "language": "English", "segments": [ { "start": 0.0, "end": 5.2, "text": "Hello, this is a sample", "speaker": "A", "words": [ { "start": 0.0, "end": 0.8, "text": "Hello" } ] } ] } }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

5. Get Transcription Status#

Check the current status of a transcription.

Endpoint: GET /api/v1/transcriptions/{id}/status

Example:

GET /api/v1/transcriptions/1234567890/status X-API-Key: your_api_key

Response:

{ "success": true, "data": { "id": "1234567890", "status": "processing", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:33:00Z", "error_message": null }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

Failed Status Response:

{ "success": true, "data": { "id": "1234567890", "status": "failed", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:32:00Z", "error_message": "Failed to download file from URL: Connection timeout" }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

6. Create YouTube Transcription#

Create a transcription from a YouTube video URL.

Endpoint: POST /api/v1/transcriptions/youtube

Request:

POST /api/v1/transcriptions/youtube Content-Type: application/json X-API-Key: your_api_key { "url": "https://www.youtube.com/watch?v=VIDEO_ID", "language_code": "en", "transcription_type": "transcript", "enable_speaker_diarization": false, "webhook_url": "https://your-webhook-url.com" }

Parameters:

  • url (string, required): YouTube video URL
  • language_code (string, optional): Language code (e.g., "en", "zh", "es"), see Language Support. Omit it, or pass "auto", to detect the language automatically
  • transcription_type (string, optional): Type of transcription
  • enable_speaker_diarization (boolean, optional): Enable speaker identification
  • webhook_url (string, optional): Webhook URL for notifications

Note: Video title and duration are automatically extracted from the YouTube URL.

Response: Same format as regular transcription creation - returns task status for async processing.

7. Get Usage#

Get your remaining transcription minutes and when your monthly minutes reset next. Any valid API key works for this endpoint. No extra scope is needed.

Endpoint: GET /api/v1/usage

Example:

GET /api/v1/usage X-API-Key: your_api_key

Response:

{ "success": true, "data": { "total_minutes": 1800, "used_minutes": 400, "remaining_minutes": 1400, "next_reset_at": "2026-11-05T08:12:30.000Z", "entitlements": [ { "source_type": "subscription", "is_recurring": true, "total_minutes": 1200, "used_minutes": 300, "remaining_minutes": 900, "valid_from": "2026-10-05T08:12:30.000Z", "valid_until": "2026-11-05T08:12:30.000Z" }, { "source_type": "one_time", "is_recurring": false, "total_minutes": 600, "used_minutes": 100, "remaining_minutes": 500, "valid_from": "2026-09-01T02:00:00.000Z", "valid_until": "2027-09-01T02:00:00.000Z" } ] }, "message": "Success", "timestamp": "2026-10-21T07:00:00Z" }

Fields:

  • total_minutes / used_minutes / remaining_minutes (integer): Totals across all of your active entitlements. remaining_minutes is the number of minutes you can still transcribe.
  • next_reset_at (string or null): When your monthly minutes reset next. This is the earliest valid_until among your recurring entitlements. It is null when nothing you have resets, for example when you only have one-time packs.
  • entitlements (array): One item for each active source of minutes, ordered by expiry with the soonest first.
    • source_type: subscription, ltd, free_plan, one_time, bonus or referral
    • is_recurring: true for monthly minutes that reset. false for minutes that never reset and expire at valid_until, such as one-time packs and bonuses.
    • total_minutes, used_minutes, remaining_minutes: Minutes for this item only
    • valid_from, valid_until: When this item became valid and when it expires

Timezone: All timestamps are in UTC (ISO 8601 with a Z suffix). Convert them to your local timezone yourself.

How monthly minutes reset:

  • Minutes do not reset at midnight or on the 1st of the month. Each account resets on its own cycle:
    • Monthly subscriptions: Follow your Stripe billing period. next_reset_at is the end of the current billing period. The new allowance is granted once the renewal payment succeeds.
    • Yearly subscriptions, lifetime (LTD) plans and Free: Reset every month, one month after the previous reset. The first cycle starts when the plan starts.
  • When next_reset_at passes, unused minutes from that recurring entitlement do not carry over. You get a fresh monthly allowance. One-time packs and bonuses do not reset.
  • Yearly, LTD and Free resets are processed by a scheduled job that runs every 5 minutes. The new allowance can therefore show up as much as about 5 minutes after next_reset_at, and the reset time can move a few minutes later from one month to the next. Always read next_reset_at instead of computing the reset time yourself.

Status Values#

Transcriptions progress through the following statuses:

  • queued: Task is queued for processing
  • preprocessing: Media file is being preprocessed (for large files or youtube videos)
  • processing: Transcription is in progress
  • completed: Transcription is complete
  • failed: Transcription failed

Webhook Notifications#

When you provide a webhook_url, UniScribe will send HTTP POST notifications to your endpoint when transcription completes or fails.

Webhook Events#

Transcription Completed#

{ "event": "transcription.completed", "data": { "id": "1234567890", "filename": "audio.mp3", "status": "completed", "duration": 120.5, "language_code": "en", "transcription_type": "transcript", "created_at": "2024-01-15T10:30:00Z", "completed_at": "2024-01-15T10:35:00Z", "file_size": 1048576, "source_type": "upload" }, "timestamp": "2024-01-15T10:35:00Z" }

Transcription Failed#

{ "event": "transcription.failed", "data": { "id": "1234567890", "filename": "audio.mp3", "status": "failed", "created_at": "2024-01-15T10:30:00Z", "completed_at": "2024-01-15T10:32:00Z", "error_message": "Failed to download file from URL: Connection timeout" }, "timestamp": "2024-01-15T10:32:00Z" }

Webhook Requirements#

  • Your webhook endpoint must respond with HTTP 2xx status code
  • Timeout: 30 seconds
  • Include User-Agent: UniScribe-Webhook/1.0 header

Error Handling#

Understanding Different Types of Errors#

UniScribe API distinguishes between two types of errors that require different handling approaches:

1. API Call Errors#

These occur when the API request itself fails (authentication, validation, system errors, etc.). The response will have success: false:

{ "success": false, "error": { "message": "Invalid API Key", "code": 41001, "details": "The provided API key is not valid" }, "timestamp": "2024-01-15T10:30:00Z" }

2. Async Task Failures#

These occur when the API call succeeds but the transcription/preprocessing task fails during execution. The response will have success: true but data.status: "failed":

{ "success": true, "data": { "id": "1234567890", "status": "failed", "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:32:00Z", "error_message": "Failed to download file from URL: Connection timeout" }, "message": "Success", "timestamp": "2024-01-15T10:30:00Z" }

Client-Side Error Handling#

Here's the recommended approach for handling both types of errors:

JavaScript Example#

async function handleTranscriptionResponse(response) { const data = await response.json(); // First, check if the API call itself succeeded if (!data.success) { // Handle API-level errors console.error("API Error:", data.error.message); throw new Error(`API Error (${data.error.code}): ${data.error.message}`); } // API call succeeded, now check the task status if (data.data.status === "failed") { // Handle async task failures console.error("Transcription Failed:", data.data.error_message); throw new Error(`Transcription Failed: ${data.data.error_message}`); } // Success case return data.data; }

Python Example#

def handle_transcription_response(response): data = response.json() # Check API call success if not data.get('success', False): error_info = data.get('error', {}) raise APIError(f"API Error ({error_info.get('code')}): {error_info.get('message')}") # Check task status task_data = data.get('data', {}) if task_data.get('status') == 'failed': error_msg = task_data.get('error_message', 'Unknown error') raise TranscriptionError(f"Transcription Failed: {error_msg}") return task_data

Error Response Format#

API call errors follow this consistent format:

{ "success": false, "error": { "message": "Error description", "code": 41000, "details": "Additional error details (optional)" }, "timestamp": "2024-01-15T10:30:00Z" }

Common Error Codes#

HTTP StatusError CodeDescription
40010001Invalid request parameters
40320003Account has been deactivated
40330006Insufficient transcription quota
40040001Invalid YouTube URL
40340005YouTube video extraction error
50040006YouTube video download error
40341000API Access Denied (requires active subscription or LTD plan)
40141001Invalid API Key
40141002API Key Required
41341003File size exceeded (5GB limit)
40041004Invalid file format
50041005Webhook delivery error
40041006File upload URL generation failed
40041007File not found (invalid file_key)
40041008File upload expired or not completed
40441009API key not found
40941010API key limit exceeded (max 5 keys per user)
40941011API key name already exists
40445001Transcription not found
40345002Transcription permission denied
429-Rate limit exceeded (60 requests/minute, 1000 requests/day)

Rate Limits#

  • Default: 60 requests per minute, 1000 requests per day
  • Rate limits are per API key
  • Higher limits can be discussed and adjusted based on your usage requirements. Contact [email protected] if you need a higher limit
  • Exceeded limits return HTTP 429 with retry information

Supported File Formats#

  • Audio: mp3, mpeg, mpga, m4a, wav, aac, ogg, opus, flac
  • Video: mp4, webm, mov (audio will be extracted)

Language Support#

UniScribe supports 60+ languages. language_code is optional: omit it (or pass "auto") and the spoken language is detected automatically. Passing a code outside the list below returns HTTP 400.

Complete list of supported language codes:

LanguageCodeLanguageCodeLanguageCode
AfrikaansafGujarati (ગુજરાતી)guPolish (Polski)pl
Arabic (العربية)arHebrew (עברית)hePortuguese (Português)pt
Azerbaijani (Azərbaycan)azHindi (हिन्दी)hiRomanian (Română)ro
Belarusian (Беларуская)beCroatian (Hrvatski)hrRussian (Русский)ru
Bulgarian (Български)bgHaitian Creole (Kreyòl ayisyen)htSlovak (Slovenčina)sk
Bengali (বাংলা)bnHungarian (Magyar)huSlovenian (Slovenščina)sl
Bosnian (Bosanski)bsIndonesian (Bahasa Indonesia)idAlbanian (Shqip)sq
Catalan (Català)caItalian (Italiano)itSerbian (Српски)sr
Czech (Čeština)csJapanese (日本語)jaSwedish (Svenska)sv
Welsh (Cymraeg)cyKazakh (Қазақ)kkSwahili (Kiswahili)sw
Danish (Dansk)daKannada (ಕನ್ನಡ)knTamil (தமிழ்)ta
German (Deutsch)deKorean (한국어)koTelugu (తెలుగు)te
Greek (Ελληνικά)elLithuanian (Lietuvių)ltThai (ไทย)th
EnglishenLatvian (Latviešu)lvTagalog (Tagalog/Filipino)tl
Spanish (Español)esMacedonian (Македонски)mkTurkish (Türkçe)tr
Estonian (Eesti)etMalayalam (മലയാളം)mlUkrainian (Українська)uk
Basque (Euskara)euMarathi (मराठी)mrUrdu (اردو)ur
Persian (فارسی)faMalay (Bahasa Melayu)msVietnamese (Tiếng Việt)vi
Finnish (Suomi)fiDutch (Nederlands)nlChinese (Cantonese) (粵語)yue
French (Français)frNorwegian (Norsk)noChinese (Simplified) (中文 简体)zh
Galician (Galego)glPunjabi (ਪੰਜਾਬੀ)paChinese (Taiwanese Mandarin) (國語)zh_tw

Integration Examples#

n8n Workflow#

  1. HTTP Request Node:

    • Method: POST
    • URL: https://api.uniscribe.co/api/v1/transcriptions
    • Headers: X-API-Key: your_api_key
    • Body: JSON with file_url, optional filename, and webhook_url
  2. Webhook Node:

    • Listen for completion notifications
    • Process transcription results

Python Example#

import requests # Create transcription response = requests.post( 'https://api.uniscribe.co/api/v1/transcriptions', headers={'X-API-Key': 'your_api_key'}, json={ 'file_url': 'https://example.com/audio.mp3', 'filename': 'my-recording.mp3', 'language_code': 'en', 'webhook_url': 'https://your-app.com/webhook' } ) transcription = response.json() transcription_id = transcription['data']['id'] # Check status status_response = requests.get( f'https://api.uniscribe.co/api/v1/transcriptions/{transcription_id}/status', headers={'X-API-Key': 'your_api_key'} ) print(status_response.json())

cURL Examples#

# 1. Get upload URLs curl -X POST https://api.uniscribe.co/api/v1/files/upload-url \ -H "X-API-Key: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "filename": "audio.mp3", "file_size": 1048576, "upload_expires_in": 3600, "download_expires_in": 1800 }' # 2. Upload file using the returned upload_url curl -X PUT "https://r2.cloudflare.com/bucket/12345-abc123.mp3?X-Amz-Signature=..." \ -H "Content-Type: audio/mpeg" \ --data-binary @audio.mp3 # 3. Create transcription using file_key curl -X POST https://api.uniscribe.co/api/v1/transcriptions \ -H "X-API-Key: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "file_key": "12345-abc123def456.mp3", "language_code": "en", "webhook_url": "https://your-webhook.com" }' # Alternative: Create transcription from external URL curl -X POST https://api.uniscribe.co/api/v1/transcriptions \ -H "X-API-Key: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "file_url": "https://example.com/audio.mp3", "filename": "my-audio.mp3", "language_code": "en", "webhook_url": "https://your-webhook.com" }' # Get transcription results curl -X GET https://api.uniscribe.co/api/v1/transcriptions/1234567890 \ -H "X-API-Key: your_api_key" # Check remaining minutes and next reset time (UTC) curl -X GET https://api.uniscribe.co/api/v1/usage \ -H "X-API-Key: your_api_key"

Best Practices#

  1. File Upload Strategy:

    • Use direct file upload (/api/v1/files/upload-url) for better performance and reliability
    • External URLs are suitable for files already hosted elsewhere
    • Set appropriate expiration times for upload URLs (shorter is more secure)
  2. Webhook Reliability: Always implement webhook endpoints with proper error handling and idempotency

  3. File Size: Ensure files do not exceed 5GB limit

  4. Language Detection: language_code is optional and the language is auto-detected when omitted, but specify it when known for better accuracy

  5. Status Polling: If not using webhooks, poll status endpoint every 30-60 seconds

  6. Error Handling:

    • Distinguish between API call errors (success: false) and task failures (success: true, status: "failed")
    • Implement retry logic for network errors and rate limits
    • For task failures, check error_message for specific failure reasons
    • Some task failures may be retryable (network issues), others may not (invalid file format)
  7. File Management:

    • Use file_key immediately after upload to avoid expiration issues
    • Temporary uploaded files are automatically cleaned up after 48 hours if not used
    • Files used for transcription become permanent and follow normal lifecycle
  8. Security: Never expose API keys in client-side code or public repositories

Support#

For technical questions or integration assistance, please contact our support team.