Normalize video for social media
Users upload one video. Your app needs three: 9:16 for stories and reels, 1:1 for feed posts, 16:9 for YouTube. One input, three tasks.
โPostmarkโ (a social media scheduler like Later) normalizes every upload into all three formats automatically.
Target sizes
| Platform | Aspect ratio | Dimensions | Use case |
|---|---|---|---|
| TikTok / Reels / Stories | 9:16 | 1080x1920 | Vertical short-form |
| Instagram / Facebook Feed | 1:1 | 1080x1080 | Square feed posts |
| YouTube / Web | 16:9 | 1920x1080 | Landscape long-form |
API
Vertical for TikTok/Reels (9:16):
ittybit video \
-i https://postmark-app.com/uploads/campaign-video.mov \
--width 1080 \
--height 1920 \
--fit cover \
--format mp4const task = {
input: 'https://postmark-app.com/uploads/campaign-video.mov',
kind: 'video',
options: {
width: 1080,
height: 1920,
fit: 'cover',
format: '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": "https://postmark-app.com/uploads/campaign-video.mov",
"kind": "video",
"options": {
"width": 1080,
"height": 1920,
"fit": "cover",
"format": "mp4",
},
}
res = requests.post(
"https://api.ittybit.com/jobs",
headers={"Authorization": f"Bearer {api_key}"},
json=task,
)
data = res.json()TASK='{
"input": "https://postmark-app.com/uploads/campaign-video.mov",
"kind": "video",
"options": {
"width": 1080,
"height": 1920,
"fit": "cover",
"format": "mp4"
}
}'
curl -X POST https://api.ittybit.com/jobs \
-H "Authorization: Bearer $ITTYBIT_API_KEY" \
-H "Content-Type: application/json" \
-d "$TASK" Square for feed (1:1):
{
"input": "https://postmark-app.com/uploads/campaign-video.mov",
"kind": "video",
"options": { "width": 1080, "height": 1080, "fit": "cover", "format": "mp4" }
}
Landscape for YouTube (16:9):
{
"input": "https://postmark-app.com/uploads/campaign-video.mov",
"kind": "video",
"options": { "width": 1920, "height": 1080, "fit": "cover", "format": "mp4" }
}
CLI
ittybit video \
-i campaign-video.mov \
-o reels.mp4 \
--width 1080 \
--height 1920 \
--fit cover
ittybit video \
-i campaign-video.mov \
-o square.mp4 \
--width 1080 \
--height 1080 \
--fit cover
ittybit video \
-i campaign-video.mov \
-o youtube.mp4 \
--width 1920 \
--height 1080 \
--fit cover
Fit modes
| Fit | Behavior | Best for |
|---|---|---|
cover | Fills frame, crops edges | Social media โ no letterboxing |
contain | Fits inside frame, may letterbox | Preserving full frame |
fill | Stretches to exact dimensions | Rarely useful |
Use cover for social. It fills the target frame and crops the overflow โ no black bars.
All three in one script
Fire all three tasks in parallel. Each runs independently.
const formats = [
{ name: 'reels', width: 1080, height: 1920 },
{ name: 'square', width: 1080, height: 1080 },
{ name: 'youtube', width: 1920, height: 1080 },
];
const tasks = await Promise.all(
formats.map(async ({ width, height }) => {
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({
input: 'https://postmark-app.com/uploads/campaign-video.mov',
kind: 'video',
options: { width, height, fit: 'cover', format: 'mp4' },
}),
});
return res.json();
}),
);
Three tasks, three outputs, one upload. Poll each task ID or use webhooks to get notified when they finish.