Skip to content

Preview Videxo runs jobs, accepts uploads up to 100 MB, serves output downloads and sends signed webhooks. Sign in with email, GitHub or a passkey. Plans and top-ups are not on sale yet. Read the quickstart

A JSON API for video files

Video processing API. For files, not live streams.

Transcode, compress, trim, join, make thumbnails, GIFs, audio and transcripts. Send one JSON request per job from curl, the TypeScript SDK or any HTTP client; Videxo runs it, stores the files and calls your webhook.

Processes files
A video file in, new files out, each with a download link.
Does not stream
No live video, player or playback hosting.
curl "$VIDEXO_API_URL/v1/transcode" \
  -H "Authorization: Bearer $VIDEXO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "input": "https://example.com/upload.mov",
  "filename": "clip-720p.mp4",
  "trim": {
    "start": 5,
    "duration": 30
  },
  "resize": {
    "width": 1280
  },
  "video": {
    "codec": "h264",
    "crf": 26
  },
  "webhook_url": "https://example.com/webhooks/videxo"
}'

Operations

11 endpoints. One JSON request each.

  1. 01

    Transcode

    Convert, compress, resize, trim, crop, watermark and more, one video in, one out.

    Writes

    • clip-720p.mp4

    POST /v1/transcode

    {
      "input": "https://example.com/upload.mov",
      "filename": "clip-720p.mp4",
      "trim": { "start": 5, "duration": 30 },
      "resize": { "width": 1280 },
      "video": { "codec": "h264", "crf": 26 },
      "webhook_url": "https://example.com/webhooks/videxo"
    }
  2. 02

    Thumbnails

    Frames as JPEG or PNG images, at the times you name or the most typical one.

    Writes

    • thumbnail_1.jpg
    • thumbnail_2.jpg
    • thumbnail_3.jpg

    POST /v1/thumbnails

    {
      "input": "https://example.com/upload.mov",
      "times": [1, 15, 30],
      "resize": { "width": 640 }
    }
  3. 03

    Storyboard

    Frames at an interval laid out in a grid on one image.

    Writes

    • storyboard.jpg

    POST /v1/storyboard

    {
      "input": "https://example.com/upload.mov",
      "interval": 10,
      "columns": 5,
      "rows": 4,
      "tile_width": 320
    }
  4. 04

    GIF

    A few seconds as an animated GIF with a palette made for its frames.

    Writes

    • preview.gif

    POST /v1/gif

    {
      "input": "https://example.com/upload.mov",
      "filename": "preview.gif",
      "trim": { "start": 5, "duration": 4 },
      "fps": 12,
      "resize": { "width": 480 }
    }
  5. 05

    Audio

    The sound as MP3, AAC, Opus, Ogg, FLAC or WAV, normalised or mixed.

    Writes

    • voice.mp3

    POST /v1/audio

    {
      "input": "https://example.com/upload.mov",
      "filename": "voice.mp3",
      "audio": { "normalize": true, "bitrate": "192k" }
    }
  6. 06

    Join

    Clips and parts of clips one after another in one file.

    Writes

    • with-intro.mp4

    POST /v1/concat

    {
      "inputs": [
        {
          "source": "https://example.com/intro.mp4",
          "start": 0,
          "end": 5
        },
        {
          "source": "https://example.com/upload.mov",
          "start": 10,
          "end": 40,
          "transition": { "type": "fade", "duration": 1 }
        }
      ],
      "resize": { "width": 1280, "height": 720 },
      "filename": "with-intro.mp4"
    }
  7. 07

    Slideshow

    Still images as one video, each held for a time, with a slow zoom or pan and music.

    Writes

    • slideshow.mp4

    POST /v1/slideshow

    {
      "images": [
        "https://example.com/photo-1.jpg",
        "https://example.com/photo-2.jpg"
      ],
      "duration": 4,
      "motion": "zoom-in",
      "resize": { "width": 1280, "height": 720 },
      "extra_inputs": { "music": "https://example.com/music.mp3" },
      "audio": { "input": "music" },
      "filename": "slideshow.mp4"
    }
  8. 08

    Scenes

    A JSON list of the scenes in a video: where each starts and ends, and how sharp the change was.

    Writes

    • scenes.json

    POST /v1/scenes

    {
      "input": "https://example.com/upload.mov",
      "threshold": 12
    }
  9. 09

    Transcribe

    What is said in English or Polish, as JSON with times, SubRip and WebVTT.

    Writes

    • transcript.json
    • transcript.srt
    • transcript.vtt

    POST /v1/transcribe

    {
      "input": "https://example.com/upload.mov",
      "language": "en"
    }
  10. 10

    Probe

    The container, duration and streams of a file as JSON, read with ffprobe. Nothing is encoded.

    Writes

    • metadata.json

    POST /v1/probe

    { "input": "https://example.com/upload.mov" }
  11. 11

    Several outputs

    Many outputs from the same inputs in one job: renditions, a poster and a preview.

    Writes

    • hd.mp4
    • sd.mp4
    • poster.jpg
    • preview.gif

    POST /v1/jobs

    {
      "inputs": { "upload": "https://example.com/upload.mov" },
      "outputs": [
        { "name": "hd", "preset": "web-1080p" },
        { "name": "sd", "preset": "web-720p" },
        { "name": "poster", "preset": "poster" },
        { "name": "preview", "preset": "gif-preview" }
      ],
      "webhook_url": "https://example.com/webhooks/videxo"
    }

Each body is compiled at build time as the API compiles it; the files are what its job writes. Several outputs from one upload? The last request does that in one job.

Webhooks

No polling loop. Your route hears when the job ends.

From the request to the signed webhook and the download, in 6 stepsYour backendYour webhook routeVidexo1 POST /v1/transcode with webhook_url2 202: the job, queued3 Fetch, run ffmpeg, store4 job.succeeded, signed5 Verify, answer 2xx6 Download the outputs
  1. 1

    POST /v1/transcode with webhook_url

    Your backend sends the JSON request with its API key. The webhook_url is a public HTTPS endpoint of yours on the default port, on your own domain.

  2. 2

    202: the job, queued

    The answer is the job with its id. Keep the id next to your own record; the request does not wait for the video.

  3. 3

    Fetch, run ffmpeg, store

    The job reads its inputs, runs and stores each output with its size, SHA-256 and expiry.

  4. 4

    job.succeeded, signed

    Videxo POSTs the whole job to your route, signed in the Standard Webhooks format. A failed job sends job.failed with an error code, a cancelled one job.cancelled.

  5. 5

    Verify, answer 2xx

    Check the signature over the raw body and answer within 10 seconds. Without a 2xx the delivery is sent again, 8 attempts in all over about a day.

  6. 6

    Download the outputs

    Each output carries a signed download URL that lasts up to one hour. Read the job again for a fresh one.

Events
job.succeeded job.failed job.cancelled
Signature
HMAC-SHA256 in webhook-signature, with webhook-id and webhook-timestamp.
Retries
8 attempts over about a day, the same webhook-id on each.

TypeScript SDK

Typed requests. Webhooks checked for you.

@videxo/sdk has a method per endpoint, with types generated from the API's OpenAPI document. It runs on Node 20 or newer, Bun, Deno and Cloudflare Workers, retries what is safe to retry and verifies webhook signatures.

npm i @videxo/sdk

Another language?

Each guide creates a job and verifies its webhook in that stack.

import { Videxo } from '@videxo/sdk';

const videxo = new Videxo({ apiKey: process.env.VIDEXO_API_KEY, baseUrl: process.env.VIDEXO_API_URL });

const job = await videxo.transcode({
  "input": "https://example.com/upload.mov",
  "filename": "clip-720p.mp4",
  "trim": {
    "start": 5,
    "duration": 30
  },
  "resize": {
    "width": 1280
  },
  "video": {
    "codec": "h264",
    "crf": 26
  },
  "webhook_url": "https://example.com/webhooks/videxo"
});
const done = await videxo.jobs.wait(job.id);

The same transcode as at the top. jobs.wait polls until it ends, for scripts without a webhook.

Files in, files out

It makes files. You decide where they play.

Videxo is for the work between an upload and your storage. It does not run live streams, a player or playback hosting.

  • Adaptive streaming (HLS) and a player Soon

    Playback that adapts to the connection, with an embeddable player.

  • Signed playback Soon

    Playback links that only work for the viewers you sign them for.

Inputs
Public http or https URLs, or private uploads of up to 100 MB. Up to 1 GB to 10 GB of inputs a job, by plan.
Outputs
Stored with their size, SHA-256 and expiry, kept 7 days to 30 days by plan. Download links last up to one hour; delete outputs sooner with one call.
Longest job
10 min to 60 min of processing, by plan.
Price
Per minute of output by quality, plus 10 credits a GB of inputs and outputs. At the base rate, video in H.264 at up to 30 fps:
  • Video up to 720p 6 credits, $0.006 a minute
  • Video 1080p 12 credits, $0.012 a minute
  • Audio 2 credits, $0.002 a minute
  • GIF 30 credits, $0.03 a minute
  • Image: JPEG, PNG or WebP (thumbnail, frame) 1 credit, $0.001 an image
  • Transcript, of the media transcribed 8 credits, $0.008 a minute
A minute of 1080p H.264 with 150 MB in and out: 12 + 1.5 = 13.5 credits, $0.0135, when ffmpeg runs no longer than 3 times the output's length. Time past that costs 10 credits a minute.

Questions, answered.

Does it stream video or host a player?

No. Videxo processes files: a video file goes in, new files come out, and your app serves them from wherever it keeps them. There is no live streaming, player or playback hosting today.

Adaptive streaming (HLS) and a player Soon and signed playback Soon are planned.

Which formats can it read and write?

It runs ffmpeg 6.1.1. Outputs can be .mp4 .mov .m4a .mkv .webm .ts .mp3 .aac .wav .flac .ogg .opus .gif .png .jpg .jpeg, with the codecs libx264, libx265, libvpx, libvpx-vp9, libaom-av1, mpeg4, mjpeg, png, gif, ffv1, aac, libmp3lame, libopus, libvorbis, flac, pcm_s16le, pcm_s24le, pcm_f32le, alac. It reads MP4, MOV, MKV, WebM, AVI and MPEG-TS video, MP3, AAC, WAV, FLAC and Ogg audio, and GIF, PNG, JPEG, WebP and BMP images.

Do I need to know ffmpeg?

No. Each job is JSON: a preset such as web-720p, or options such as trim, resize and crf. A job that runs ffmpeg returns its command as ffmpeg_command; steps lists every tool a job runs, such as ffprobe for a probe or whisper for a transcript, where ffmpeg_command is null. If you already have ffmpeg commands, POST /v1/jobs takes them as they are.

Which languages can call it?

Any language with an HTTP client. The TypeScript SDK is on npm as @videxo/sdk, a Swift package covers iOS and macOS apps, and the docs have complete examples for Express, Next.js, SvelteKit, FastAPI, Go, PHP, Laravel, Rails, Swift and Kotlin.

What happens when a job fails?

It ends failed with a documented error code and a sentence that says what to change, and your webhook gets job.failed. A request the API cannot run is refused at once: 400 invalid_request, with each field at fault.

What does it cost to try?

Pay as you go needs no card, and your personal organization gets 500 free credits once, valid 30 days. A minute of 1080p H.264 with 150 MB in and out is 13.5 credits, $0.0135 at the base rate, when ffmpeg runs no longer than 3 times the output's length; time past that costs 10 credits a minute.

Run your first job. Free, no card.

500 free credits to start, no card.