# Normalize video for social media

Auto-resize and crop video for Instagram, TikTok, and YouTube

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):

<CodeGroup labels={["CLI", "TypeScript", "Python", "curl"]}>
```bash
ittybit video \
  -i https://postmark-app.com/uploads/campaign-video.mov \
  --width 1080 \
  --height 1920 \
  --fit cover \
  --format mp4
```

```typescript
const 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();
```

```python

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()
```

```bash
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"
```

</CodeGroup>

Square for feed (1:1):

```json
{
  "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):

```json
{
  "input": "https://postmark-app.com/uploads/campaign-video.mov",
  "kind": "video",
  "options": { "width": 1920, "height": 1080, "fit": "cover", "format": "mp4" }
}
```

## CLI

```bash
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.

```typescript
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.