RunConvert
REST APIv1

API Reference

Convert any file programmatically. Import files, apply conversions, and export results - all in a single API call.

Base URLapi.runconvert.com/v3
Get API Key
Intro

Overview

The RunConvert API lets you convert any file programmatically. Create jobs with chained tasks - import a file, convert it, and export the result - all in a single request.

Every pipeline follows the same shape: import → convert → export. Tasks reference each other by name using the input field, and the API executes them in dependency order automatically.

Base URL

HTTPShttps://api.runconvert.com/v3

Request format

All request bodies must be JSON. Set Content-Type: application/json on every POST/PATCH request.

Discover supported formats

Call GET /v3/operations (no auth required) to find which input formats are supported, what each can be converted to, and which engines and options are available. See the Discover formats section for full details.

minimal-job.sh
# Convert mp4  mp3 in one request
curl -X POST https://api.runconvert.com/v3/jobs \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": {
      "import-1": {
        "operation": "import/url",
        "url": "https://example.com/video.mp4"
      },
      "convert-1": {
        "operation": "convert",
        "input": "import-1",
        "input_format": "mp4",
        "output_format": "mp3"
      },
      "export-1": {
        "operation": "export/url",
        "input": "convert-1"
      }
    }
  }'
Intro

Authentication

All API requests require bearer token authentication. Include your API key in the Authorization header of every request.

Never expose API keys in client-side code or public repositories. Use environment variables and keep keys server-side only.

Revoking keys

Revoke any key from Dashboard → API Keys. Revoked keys return 401 immediately.

bash
curl https://api.runconvert.com/v3/jobs \
  -H "Authorization: Bearer sk_live_abc123..." \
  -H "Content-Type: application/json"
401.json
{
  "code": "UNAUTHORIZED",
  "message": "Sign in to view jobs."
}
Intro

Quickstart

Convert an MP4 video to MP3 audio from a public URL in four steps.

01

Import

Define an import/url task pointing at your source file.

02

Convert

Add a convert task. Both input_format and output_format are required.

03

Export

Add an export/url task to get a download link for the output.

04

Poll or webhook

Poll GET /jobs/{id} until status is "finished", or configure a webhook to receive a callback.

Not sure which formats are supported? Call GET /v3/operations?input=mp4 to see all valid output formats for any input extension, along with credit costs.

create-job.sh
curl -X POST https://api.runconvert.com/v3/jobs \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "tag": "mp4-to-mp3",
    "tasks": {
      "import-1": {
        "operation": "import/url",
        "url": "https://example.com/video.mp4"
      },
      "convert-1": {
        "operation": "convert",
        "input": "import-1",
        "input_format": "mp4",
        "output_format": "mp3"
      },
      "export-1": {
        "operation": "export/url",
        "input": "convert-1"
      }
    }
  }'
response.json
// Created - tasks are dispatched immediately
{
  "id": "job_01hx4k9m2p",
  "status": "processing",
  "tag": "mp4-to-mp3",
  "credits_used": 1,
  "created_at": "2026-05-01T10:23:00Z"
}

// Poll GET /v3/jobs/job_01hx4k9m2p?include=tasks
// until status === "finished"
{
  "id": "job_01hx4k9m2p",
  "status": "finished",
  "credits_used": 1,
  "started_at": "2026-05-01T10:23:00Z",
  "ended_at":   "2026-05-01T10:23:04Z",
  "expires_at":  "2026-05-02T10:23:04Z",
  "tasks": [
    {
      "name": "export-1",
      "operation": "export/url",
      "status": "finished",
      "result": {
        "files": [{
          "filename": "video.mp3",
          "url": "https://cdn.runconvert.com/dl/...",
          "size": 4821200,
          "created_at": "2026-05-01T10:23:03Z"
        }]
      }
    }
  ]
}
Core

Jobs

A job is a container for one or more named tasks. Tasks reference each other by name via the input field. The API resolves the dependency graph and executes tasks in the correct order.

Job statuses

waitingJob is queued, waiting for a worker to pick it up
processingOne or more tasks are actively running
finishedAll tasks completed successfully
errorOne or more tasks failed (credits are refunded for API key jobs)

Create a job

POST/v3/jobs
ParamTypeDescription
tasksreqobjectNamed task definitions. Keys are task names referenced by downstream tasks via input. Names must be alphanumeric with hyphens or underscores (max 64 chars).
tagstringOptional label for filtering in the list endpoint.
metadataobjectArbitrary key-value data stored with the job.

List jobs

GET/v3/jobs
ParamTypeDescription
filter[status]stringFilter by status: waiting | processing | finished | error
filter[tag]stringFilter by tag.
includestringPass "tasks" to embed task objects in each job.
pageintegerPage number. Default: 1.
per_pageintegerResults per page. Default: 100.

Get a job

GET/v3/jobs/{id}
ParamTypeDescription
includestringPass "tasks" to include task objects in the response.

Cancel a job

POST/v3/jobs/{id}/cancel

Cancels all pending tasks. Credits are refunded for API key jobs.

Delete a job

DELETE/v3/jobs/{id}
jobs.sh
# List finished jobs
curl "https://api.runconvert.com/v3/jobs?filter[status]=finished&per_page=10" \
  -H "Authorization: Bearer sk_live_..."

# Get a specific job with its tasks
curl "https://api.runconvert.com/v3/jobs/job_01hx4k9m2p?include=tasks" \
  -H "Authorization: Bearer sk_live_..."

# Cancel a job
curl -X POST https://api.runconvert.com/v3/jobs/job_01hx4k9m2p/cancel \
  -H "Authorization: Bearer sk_live_..."
job-object.json
{
  "id": "job_01hx4k9m2p",
  "status": "finished",
  "tag": "mp4-to-mp3",
  "credits_used": 1,
  "credit_type": "api",
  "metadata": {},
  "created_at": "2026-05-01T10:23:00Z",
  "started_at": "2026-05-01T10:23:00Z",
  "ended_at":   "2026-05-01T10:23:04Z",
  "expires_at": "2026-05-02T10:23:04Z"
}
Core

Tasks

Tasks are the individual steps within a job. Each task has an operation plus operation-specific parameters. The input field references the upstream task name - it accepts either a string or an array of strings.

Task statuses

waitingWaiting for upstream tasks to complete
processingCurrently executing
finishedCompleted successfully
errorEncountered an error

ignore_error

Set ignore_error: true on a task to let the job continue even if that task hits an error. Downstream tasks that depend on a failed task are skipped.

List tasks

GET/v3/tasks
ParamTypeDescription
filter[job_id]stringFilter by job ID.
filter[status]stringFilter by status: waiting | processing | finished | error
filter[operation]stringFilter by operation type.
includestringComma-separated extras: retries, payload, depends_on_tasks
pageintegerPage number. Default: 1.
per_pageintegerResults per page. Default: 100.

Get / Cancel / Retry a task

GET/v3/tasks/{id}
POST/v3/tasks/{id}/cancel
POST/v3/tasks/{id}/retry

Only tasks in error status can be retried.

task-object.json
{
  "id": "task_9xkq2v",
  "job_id": "job_01hx4k9m2p",
  "name": "convert-1",
  "operation": "convert",
  "status": "finished",
  "percent": 100,
  "message": "",
  "engine": "ffmpeg",
  "engine_version": "6.1",
  "credits": 1,
  "ignore_error": false,
  "input": ["import-1"],
  "result": {
    "files": [{
      "filename": "video.mp3",
      "size": 4821200,
      "url": "https://cdn.runconvert.com/...",
      "created_at": "2026-05-01T10:23:03Z"
    }]
  },
  "created_at": "2026-05-01T10:23:00Z",
  "started_at": "2026-05-01T10:23:00Z",
  "ended_at":   "2026-05-01T10:23:03Z",
  "expires_at": "2026-05-02T10:23:03Z"
}
Import

import/url

Import a file from any publicly accessible URL. RunConvert fetches the file at job execution time.

ParamTypeDescription
operationreqstringMust be "import/url".
urlreqstringPublicly accessible URL of the file to import.
filenamestringOverride the filename. Defaults to the last segment of the URL path.
json
{
  "tasks": {
    "import-1": {
      "operation": "import/url",
      "url": "https://example.com/video.mp4",
      "filename": "video.mp4"
    },
    "convert-1": {
      "operation": "convert",
      "input": "import-1",
      "input_format": "mp4",
      "output_format": "mp3"
    }
  }
}
Import

import/upload

Get a presigned upload URL for direct file uploads. Create the job - the API returns an upload_url immediately in the task object. PUT your file to that URL, and the job proceeds automatically once the upload is received.

ParamTypeDescription
operationreqstringMust be "import/upload".
filenamereqstringName of the file being uploaded, including extension.

The upload URL expires after a short window (5–15 minutes depending on your plan). Upload promptly after job creation.

create-job.json
{
  "tasks": {
    "import-1": {
      "operation": "import/upload",
      "filename": "video.mp4"
    },
    "convert-1": {
      "operation": "convert",
      "input": "import-1",
      "input_format": "mp4",
      "output_format": "mp3"
    },
    "export-1": { "operation": "export/url", "input": "convert-1" }
  }
}
job-response.json
// The import task immediately has an upload_url
{
  "id": "job_01hx4k9m2p",
  "status": "waiting",
  "tasks": [{
    "name": "import-1",
    "operation": "import/upload",
    "status": "waiting",
    "upload_url": "https://storage.runconvert.com/upload/task_abc?token=...",
    "upload_deadline": "2026-05-01T10:38:00Z"
  }]
}
upload.sh
# PUT the file to the upload_url
curl -X PUT "https://storage.runconvert.com/upload/task_abc?token=..." \
  -H "Content-Type: video/mp4" \
  --data-binary @video.mp4
# Job resumes automatically once upload is received
Import

import/base64

Embed a file directly in the request body as base64. Best for small files only.

ParamTypeDescription
operationreqstringMust be "import/base64".
filereqstringBase64-encoded file content.
filenamereqstringFilename including extension - used to determine input format.

For files larger than ~1 MB use import/upload or import/url to avoid large request bodies.

json
{
  "tasks": {
    "import-1": {
      "operation": "import/base64",
      "filename": "image.png",
      "file": "iVBORw0KGgoAAAANSUhEUgAA..."
    },
    "convert-1": {
      "operation": "convert",
      "input": "import-1",
      "input_format": "png",
      "output_format": "webp"
    },
    "export-1": { "operation": "export/url", "input": "convert-1" }
  }
}
Import

import/s3

Import a file directly from an AWS S3 bucket using the credentials you provide.

ParamTypeDescription
operationreqstringMust be "import/s3".
bucketreqstringS3 bucket name.
keyreqstringObject key (path) within the bucket.
regionreqstringAWS region, e.g. us-east-1.
access_key_idstringAWS access key ID.
secret_access_keystringAWS secret access key.
json
{
  "tasks": {
    "import-1": {
      "operation": "import/s3",
      "bucket": "my-media-files",
      "key": "uploads/2026/video.mp4",
      "region": "us-east-1",
      "access_key_id": "AKIAIOSFODNN7EXAMPLE",
      "secret_access_key": "wJalrXUtnFEMI/K7MDENG..."
    },
    "convert-1": {
      "operation": "convert",
      "input": "import-1",
      "input_format": "mp4",
      "output_format": "mp3"
    }
  }
}
Convert

convert

Convert a file from one format to another. Supports 3,000+ format pairs across video, audio, image, document, archive, ebook, font, and CAD categories.

Both input_format and output_format are required. Omitting either returns a 400 error. Use GET /v3/operations?input=mp4 to discover valid output formats for any input extension.

ParamTypeDescription
operationreqstringMust be "convert".
inputreqstringName of the import task whose output to convert.
input_formatreqstringSource format extension, e.g. mp4, png, docx.
output_formatreqstringTarget format extension, e.g. mp3, webp, pdf.
preferred_enginestringRequest a specific engine when multiple are available for this pair. Falls back to the default if not found.
optionsobjectEngine-specific settings (e.g. bitrate, quality, width). Fetch available options from GET /v3/operations?input=X&output=Y.
ignore_errorbooleanIf true, the job continues even if this task fails.
const res = await fetch("https://api.runconvert.com/v3/jobs", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.RUNCONVERT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tasks: {
      "import-1": {
        operation: "import/url",
        url: "https://example.com/video.mp4",
      },
      "convert-1": {
        operation: "convert",
        input: "import-1",
        input_format: "mp4",
        output_format: "mp3",
        options: { bitrate: "192k" },
      },
      "export-1": {
        operation: "export/url",
        input: "convert-1",
      },
    },
  }),
});
const job = await res.json();
console.log(job.id);
Convert

Discover formats

GET /v3/operations is a public endpoint (no auth required) that lets you discover supported formats, valid output targets for any input extension, and the engines and options available for each conversion pair.

GET/v3/operations
ParamTypeDescription
inputstringInput format extension (e.g. mp4). Returns all valid output formats plus credit cost.
outputstringCombined with input - returns available engines and their options for this specific pair.
categorystringFilter all formats by category: video | audio | image | document | ebook | presentation | spreadsheet | vector | cad | archive | font

Using options in a convert task

The engines[].options array tells you what you can pass as the options object on a convert task - including the field name, type, description, and supported values.

bash
# All output formats for mp4 (no auth needed)
curl "https://api.runconvert.com/v3/operations?input=mp4"

# Engines + options for mp4  mp3
curl "https://api.runconvert.com/v3/operations?input=mp4&output=mp3"

# All video input formats
curl "https://api.runconvert.com/v3/operations?category=video"

# All formats across all categories
curl "https://api.runconvert.com/v3/operations"
operations-response.json
// GET /v3/operations?input=mp4
{
  "format": "mp4",
  "category": "video",
  "outputs": [
    { "format": "mp3",  "category": "audio", "engine": "ffmpeg", "credits": 1 },
    { "format": "wav",  "category": "audio", "engine": "ffmpeg", "credits": 1 },
    { "format": "webm", "category": "video", "engine": "ffmpeg", "credits": 1 },
    { "format": "gif",  "category": "image", "engine": "ffmpeg", "credits": 2 }
  ]
}

// GET /v3/operations?input=mp4&output=mp3
{
  "input": "mp4",
  "output": "mp3",
  "engines": [{
    "name": "ffmpeg",
    "credits": 1,
    "options": [
      {
        "name": "bitrate",
        "type": "string",
        "description": "Audio bitrate",
        "supported_values": ["64k", "96k", "128k", "192k", "256k", "320k"]
      },
      {
        "name": "sample_rate",
        "type": "integer",
        "description": "Sample rate in Hz",
        "supported_values": [22050, 44100, 48000]
      }
    ]
  }]
}
Export

export/url

Generate a temporary public download URL for the converted file.

ParamTypeDescription
operationreqstringMust be "export/url".
inputreqstringName of the convert task to export.
filenamestringOverride the output filename.
export-result.json
// Task definition
{
  "export-1": {
    "operation": "export/url",
    "input": "convert-1",
    "filename": "converted.mp3"
  }
}

// Result in finished job
{
  "name": "export-1",
  "status": "finished",
  "result": {
    "files": [{
      "filename": "converted.mp3",
      "size": 4821200,
      "url": "https://cdn.runconvert.com/dl/abc123/converted.mp3",
      "created_at": "2026-05-01T10:23:03Z"
    }]
  }
}
Export

export/s3

Save the converted file directly to an AWS S3 bucket.

ParamTypeDescription
operationreqstringMust be "export/s3".
inputreqstringName of the convert task to export.
bucketreqstringTarget S3 bucket name.
keyreqstringObject key (path) to write in the bucket.
regionreqstringAWS region.
access_key_idstringAWS access key ID.
secret_access_keystringAWS secret access key.
aclstring"private" | "public-read". Default: "private".
json
{
  "tasks": {
    "export-1": {
      "operation": "export/s3",
      "input": "convert-1",
      "bucket": "my-output-bucket",
      "key": "converted/video.mp3",
      "region": "us-east-1",
      "access_key_id": "AKIAIOSFODNN7EXAMPLE",
      "secret_access_key": "wJalrXUtnFEMI...",
      "acl": "private"
    }
  }
}
Export

export/base64

Return the converted file inline in the job result as base64. Best for small files only.

ParamTypeDescription
operationreqstringMust be "export/base64".
inputreqstringName of the convert task to export.

Avoid export/base64 for large files - use export/url instead.

base64-result.json
{
  "name": "export-1",
  "status": "finished",
  "result": {
    "files": [{
      "filename": "image.webp",
      "content": "UklGRlYAAABXRUJQVlA4...",
      "size": 48201
    }]
  }
}
Webhooks

Webhooks

Webhooks deliver HTTP POST notifications to your endpoint when jobs and tasks change state - eliminating polling.

Supported events

job.createdA new job was accepted
job.processingThe first task in the job started running
job.finishedAll tasks completed successfully
job.errorOne or more tasks failed (without ignore_error)
task.finishedA single task completed successfully
task.errorA single task failed

Payload structure

Every delivery POSTs JSON with an event string plus either a job or task object depending on the event type.

Signature verification

Every delivery includes an X-RunConvert-Signature header - an HMAC-SHA256 of the raw request body signed with your webhook secret. Always verify this before processing the payload.

job-event.json
// event: job.finished
{
  "event": "job.finished",
  "job": {
    "id": "job_01hx4k9m2p",
    "status": "finished",
    "tag": "mp4-to-mp3",
    "credits_used": 1,
    "created_at": "2026-05-01T10:23:00Z",
    "ended_at":   "2026-05-01T10:23:04Z",
    "expires_at": "2026-05-02T10:23:04Z"
  }
}
task-event.json
// event: task.finished
{
  "event": "task.finished",
  "task": {
    "id": "task_9xkq2v",
    "job_id": "job_01hx4k9m2p",
    "name": "export-1",
    "operation": "export/url",
    "status": "finished",
    "credits": 0,
    "result": {
      "files": [{
        "filename": "video.mp3",
        "url": "https://cdn.runconvert.com/...",
        "size": 4821200
      }]
    }
  }
}
Webhooks

Create Webhook

POST/v3/webhooks
ParamTypeDescription
urlreqstringHTTPS URL that will receive POST requests.
eventsstring[]Event types to subscribe to. Omit to receive all events.
descriptionstringHuman-readable label for this endpoint.

The full secret is returned once on creation only. Store it securely - subsequent reads return a masked value.

bash
curl -X POST https://api.runconvert.com/v3/webhooks \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://myapp.com/webhooks/runconvert",
    "events": ["job.finished", "job.error"],
    "description": "Production notifications"
  }'
webhook-created.json
{
  "id": "wh_01hx4k9m2p",
  "url": "https://myapp.com/webhooks/runconvert",
  "description": "Production notifications",
  "events": ["job.finished", "job.error"],
  "secret": "whsec_abc123...",
  "enabled": true,
  "created_at": "2026-05-01T10:00:00Z",
  "updated_at": "2026-05-01T10:00:00Z"
}
Webhooks

List & Get Webhooks

GET/v3/webhooks

Returns all webhook endpoints for your account.

GET/v3/webhooks/{id}

Returns a single endpoint. The secret is masked after creation.

Test ping

POST/v3/webhooks/{id}/ping

Sends a test delivery to the endpoint URL to verify it is reachable.

webhook-object.json
{
  "id": "wh_01hx4k9m2p",
  "url": "https://myapp.com/webhooks/runconvert",
  "description": "Production notifications",
  "events": ["job.finished", "job.error"],
  "secret": "whsec_abc***",
  "enabled": true,
  "created_at": "2026-05-01T10:00:00Z",
  "updated_at": "2026-05-01T10:00:00Z"
}
Webhooks

Update Webhook

PATCH/v3/webhooks/{id}

Update any combination of fields. Only provided fields are changed.

ParamTypeDescription
urlstringNew HTTPS endpoint URL.
eventsstring[]Replace the event subscription list.
descriptionstringUpdate the label.
enabledbooleanSet to false to pause delivery without deleting the endpoint.
bash
# Pause a webhook
curl -X PATCH https://api.runconvert.com/v3/webhooks/wh_01hx4k9m2p \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

# Change subscribed events
curl -X PATCH https://api.runconvert.com/v3/webhooks/wh_01hx4k9m2p \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "events": ["job.finished", "job.error", "task.error"] }'
Webhooks

Delete Webhook

DELETE/v3/webhooks/{id}

Permanently removes a webhook endpoint. No further events will be delivered.

bash
curl -X DELETE \
  https://api.runconvert.com/v3/webhooks/wh_01hx4k9m2p \
  -H "Authorization: Bearer sk_live_..."

# 204 No Content on success
Reference

Errors

All error responses include a JSON body with a machine-readable code and a human-readable message.

HTTP status codes

200OKRequest succeeded
201CreatedResource created
204No ContentSuccessful deletion
400Bad RequestInvalid body, missing required field, or unsupported format pair
401UnauthorizedMissing or invalid API key / sign-in required
403ForbiddenResource belongs to another account
404Not FoundResource does not exist
422UnprocessableInsufficient credits, no workers available
429Too Many RequestsDaily credit limit or active job limit exceeded
500Server ErrorRetry with exponential backoff

Application error codes

UNAUTHORIZEDMissing or invalid API key
SIGN_IN_REQUIREDThis operation requires a signed-in account
VALIDATION_ERRORInvalid body or missing required field (e.g. input_format)
INVALID_OPERATIONUnknown operation or unsupported input→output pair
JOB_NOT_FOUNDJob ID does not exist
JOB_FORBIDDENJob belongs to another account
TASK_NOT_FOUNDTask ID does not exist
TASK_NOT_RETRYABLEOnly error tasks can be retried
BILLING_INSUFFICIENT_CREDITSAccount has no remaining credits
NO_WORKERS_AVAILABLENo conversion workers available right now
GUEST_DAILY_LIMIT_REACHEDGuest daily limit reached (10/day) - sign up for more
FREE_TIER_DAILY_LIMIT_REACHEDFree account daily limit reached - upgrade for unlimited
ACTIVE_JOB_LIMIT_REACHEDToo many active jobs - wait for existing ones to finish
DEVICE_BLOCKEDThis device has been blocked
error-examples.json
// 400 - missing input_format
{
  "code": "VALIDATION_ERROR",
  "message": "task \"convert-1\": input_format is required for convert tasks"
}

// 400 - unsupported format pair
{
  "code": "INVALID_OPERATION",
  "message": "operation \"convert\": conversion from \"mp4\" to \"xyz\" is not supported"
}

// 422 - insufficient credits
{
  "code": "BILLING_INSUFFICIENT_CREDITS",
  "message": "Insufficient credits."
}

// 429 - guest daily limit
{
  "code": "GUEST_DAILY_LIMIT_REACHED",
  "message": "Daily limit reached (10/day). Sign up for a free account to get more."
}
Reference

Rate Limits

RunConvert uses a credit-based daily limit system. Each conversion costs a number of credits (visible via GET /v3/operations?input=X&output=Y). API key jobs additionally deduct from your team credit balance.

Daily credit limits

TierCredits / dayNotes
Guest (no account)10Device-based; sign up for more
Free account20Upgrade for unlimited
API key (subscribed)UnlimitedGoverned by team credit balance

Active job limit

There is a per-account limit on concurrent active jobs to keep the queue healthy. Wait for existing jobs to reach finished or error before creating new ones if you hit this limit.

limit-errors.json
// 429 - guest daily limit
{
  "code": "GUEST_DAILY_LIMIT_REACHED",
  "message": "Daily limit reached (10/day). Sign up for a free account to get more."
}

// 429 - free account limit
{
  "code": "FREE_TIER_DAILY_LIMIT_REACHED",
  "message": "Daily limit reached (20/day). Upgrade for unlimited conversions."
}

// 429 - too many concurrent jobs
{
  "code": "ACTIVE_JOB_LIMIT_REACHED",
  "message": "Too many active jobs. Wait for existing jobs to finish before creating new ones."
}
Reference

SDKs & Libraries

Official SDKs are coming soon. In the meantime, the REST API works with any HTTP client in any language.

Node.js / TypeScript

Use native fetch or any HTTP library (axios, got, ky).

Python

Use requests or httpx.

Community libraries

Community-maintained SDKs will be listed here as they become available. Building one? Let us know.

Get your API key

API access is available on Premium plans.

Open Dashboard
poll-until-done.ts
async function waitForJob(jobId: string, apiKey: string) {
  while (true) {
    const res = await fetch(
      `https://api.runconvert.com/v3/jobs/${jobId}?include=tasks`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    const job = await res.json();

    if (job.status === "finished") return job;
    if (job.status === "error")    throw new Error(`Job failed: ${jobId}`);

    // Poll every 2 seconds (or use webhooks instead)
    await new Promise(r => setTimeout(r, 2000));
  }
}

const job = await waitForJob("job_01hx4k9m2p", process.env.RUNCONVERT_API_KEY!);
const exportTask = job.tasks.find((t: any) => t.operation === "export/url");
console.log(exportTask.result.files[0].url);