Replace AWS MediaConvert with Ittybit

View Markdown

MediaConvert works, but the setup tax is real: IAM roles, job templates, output presets, queue configuration, CloudWatch alarms for failures, S3 event notifications to trigger jobs. Each piece has its own console, its own API, its own failure modes. Ittybit replaces all of that with a single POST. Your files stay in S3 — Ittybit reads from your bucket, processes the media, and writes the output back.

What you’re replacing

A typical MediaConvert pipeline looks like this:

IAM role (MediaConvert execution role)
  -> IAM policy (S3 read/write, CloudWatch logs)
    -> Job template (codec, container, resolution, bitrate)
      -> Output preset (per rendition)
        -> Queue (on-demand or reserved)
          -> S3 event notification -> Lambda -> CreateJob API
            -> CloudWatch alarm (job failure)
              -> SNS topic -> email / Slack

That’s at least 8 resources to create, configure, and maintain before a single frame gets transcoded.

With Ittybit, the entire pipeline is:

S3 bucket -> Ittybit connection (once) -> POST /jobs

Set up your S3 connection

Register your bucket with Ittybit once. This lets Ittybit read inputs from and write outputs to your S3 bucket.

ittybit connections add s3 \
  --name my-media-bucket \
  --endpoint https://s3.us-east-1.amazonaws.com \
  --region us-east-1 \
  --access-key-id $AWS_ACCESS_KEY_ID \
  --secret-access-key $AWS_SECRET_ACCESS_KEY

Note the connection_id returned — you’ll pass it in your task options.

Video transcode

MediaConvert requires a job template with codec settings, container format, resolution, and a queue. Here’s the equivalent Ittybit call.

const task = {
  input: "s3://my-media-bucket/uploads/raw.mov",
  kind: "video",
  options: {
    connection_id: "conn_abc123",
    width: 1920,
    format: "mp4",
    quality: "high",
  },
  output: "s3://my-media-bucket/processed/raw.mp4",
};

const res = await fetch("https://api.ittybit.com/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "s3://my-media-bucket/uploads/raw.mov",
    "kind": "video",
    "options": {
        "connection_id": "conn_abc123",
        "width": 1920,
        "format": "mp4",
        "quality": "high",
    },
    "output": "s3://my-media-bucket/processed/raw.mp4",
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "s3://my-media-bucket/uploads/raw.mov",
  "kind": "video",
  "options": {
    "connection_id": "conn_abc123",
    "width": 1920,
    "format": "mp4",
    "quality": "high"
  },
  "output": "s3://my-media-bucket/processed/raw.mp4"
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"

No job template. No output preset. No queue. The output field writes the result straight back to S3.

Audio extraction

MediaConvert audio-only jobs need a separate job template with an audio-only output group. With Ittybit, change kind to audio.

const task = {
  input: "s3://my-media-bucket/uploads/interview.mov",
  kind: "audio",
  options: {
    connection_id: "conn_abc123",
    format: "mp3",
    quality: "medium",
  },
  output: "s3://my-media-bucket/audio/interview.mp3",
};

const res = await fetch("https://api.ittybit.com/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "s3://my-media-bucket/uploads/interview.mov",
    "kind": "audio",
    "options": {
        "connection_id": "conn_abc123",
        "format": "mp3",
        "quality": "medium",
    },
    "output": "s3://my-media-bucket/audio/interview.mp3",
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "s3://my-media-bucket/uploads/interview.mov",
  "kind": "audio",
  "options": {
    "connection_id": "conn_abc123",
    "format": "mp3",
    "quality": "medium"
  },
  "output": "s3://my-media-bucket/audio/interview.mp3"
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"

Adaptive bitrate (HLS)

MediaConvert’s ABR workflows require an Apple HLS or DASH output group, multiple output presets (one per rendition), and careful bitrate ladder configuration. Ittybit handles the rendition ladder automatically.

const task = {
  input: "s3://my-media-bucket/uploads/keynote.mov",
  kind: "adaptive_video",
  options: {
    connection_id: "conn_abc123",
    format: "hls",
  },
  output: "s3://my-media-bucket/streaming/keynote/",
};

const res = await fetch("https://api.ittybit.com/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(task),
});
const data = await res.json();
import requests

task = {
    "input": "s3://my-media-bucket/uploads/keynote.mov",
    "kind": "adaptive_video",
    "options": {
        "connection_id": "conn_abc123",
        "format": "hls",
    },
    "output": "s3://my-media-bucket/streaming/keynote/",
}

res = requests.post(
    "https://api.ittybit.com/jobs",
    headers={"Authorization": f"Bearer {api_key}"},
    json=task,
)
data = res.json()
TASK='{
  "input": "s3://my-media-bucket/uploads/keynote.mov",
  "kind": "adaptive_video",
  "options": {
    "connection_id": "conn_abc123",
    "format": "hls"
  },
  "output": "s3://my-media-bucket/streaming/keynote/"
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"

The manifest and segments land in your S3 prefix. Point your player at the .m3u8 and it handles quality switching automatically.

Replacing the Lambda trigger

If you have a Lambda that calls CreateJob on MediaConvert when files hit S3, swap the MediaConvert call for an Ittybit call. The Lambda, EventBridge rule, and S3 notification stay the same — only the job creation changes.

Before (MediaConvert):

import { MediaConvertClient, CreateJobCommand } from '@aws-sdk/client-mediaconvert';

const mc = new MediaConvertClient({ endpoint: MEDIACONVERT_ENDPOINT });

await mc.send(
  new CreateJobCommand({
    Role: MEDIACONVERT_ROLE_ARN,
    Settings: {
      Inputs: [{ FileInput: `s3://${bucket}/${key}` }],
      OutputGroups: [
        {
          OutputGroupSettings: {
            Type: 'FILE_GROUP_SETTINGS',
            FileGroupSettings: { Destination: `s3://${bucket}/processed/` },
          },
          Outputs: [
            {
              ContainerSettings: { Container: 'MP4' },
              VideoDescription: {
                Width: 1920,
                CodecSettings: {
                  Codec: 'H_264',
                  H264Settings: { RateControlMode: 'QVBR', QualityTuningLevel: 'SINGLE_PASS_HQ' },
                },
              },
              AudioDescriptions: [
                {
                  CodecSettings: {
                    Codec: 'AAC',
                    AacSettings: { Bitrate: 128000, SampleRate: 48000 },
                  },
                },
              ],
            },
          ],
        },
      ],
    },
  }),
);

After (Ittybit):

await fetch('https://api.ittybit.com/jobs', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${ITTYBIT_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    input: `s3://${bucket}/${key}`,
    kind: 'video',
    options: {
      connection_id: CONNECTION_ID,
      width: 1920,
      format: 'mp4',
      quality: 'high',
    },
    output: `s3://${bucket}/processed/${key.replace(/\.[^.]+$/, '.mp4')}`,
  }),
});

You can also remove the MediaConvert execution IAM role, the CloudWatch alarm, and the SNS topic. Ittybit handles retries internally and can notify you via webhooks on completion or failure.

What you can delete

Once you’ve confirmed the Ittybit pipeline is working:

AWS ResourceWhy it existed
MediaConvert job template(s)Codec/resolution/container config
MediaConvert output preset(s)Per-rendition settings
MediaConvert queueJob scheduling
IAM role + policyMediaConvert execution permissions
CloudWatch alarmJob failure detection
SNS topic + subscriptionFailure notifications

Keep your S3 bucket, EventBridge rule, and Lambda — those still work, just with a simpler job creation call.

See also