# Upload a file

`POST https://api.vidmoat.com/v1/media/uploads`

- Scope: `media.write`
- Cost: upload cap

Upload a file directly, as multipart form data, up to 32 MB per request and within your plan's upload and storage limits. For larger files, import from a URL.

### Form fields

- `file` (file, required): The video, image or audio file.

## Response

Returns `201`.

### Response fields

- `media` (object): The stored file.
  - `url` (string): Use as a clip `src`.
  - `filename` (string): Stored name.
  - `bytes` (integer): Size.
  - `deduplicated` (boolean): `true` when you had already uploaded the same bytes; the existing file is returned.
  - `probe` (object or null): `{ width, height, fps, duration, hasAudio }`.
  - `converted` (object): Present when video the browser cannot play (HEVC, for example) was converted: `{ from, to }`.
  - `warning` (string): Present when that conversion failed.
- `hint` (string): What to do with it.

> **Tip: With a test key.** Test keys upload for real.

## Errors

Besides the errors any request can get ([authentication](https://developer.vidmoat.com/developer/docs/authentication#when-a-request-is-refused), [rate limits](https://developer.vidmoat.com/developer/docs/rate-limits)):

| Status | Code | When |
| --- | --- | --- |
| 400 | [`invalid_request`](https://developer.vidmoat.com/developer/docs/errors#invalid_request) | The body is not multipart, or `file` is missing or empty. |
| 413 | [`payload_too_large`](https://developer.vidmoat.com/developer/docs/errors#payload_too_large) | Over 32 MB, or over the plan's upload cap. |
| 413 | [`quota_exceeded`](https://developer.vidmoat.com/developer/docs/errors#quota_exceeded) | Cloud storage on the plan is full. |

## Request examples

```bash curl
curl -X POST https://api.vidmoat.com/v1/media/uploads \
  -H "Authorization: Bearer $VIDMOAT_KEY" \
  -F "file=@clip.mp4"
```

```js Node
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append('file', new Blob([await readFile('clip.mp4')]), 'clip.mp4');

const res = await fetch('https://api.vidmoat.com/v1/media/uploads', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.VIDMOAT_KEY}`,
  },
  body: form,
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? `HTTP ${res.status}`);
console.log(data);
```

```python Python
import os
import requests

res = requests.post(
    "https://api.vidmoat.com/v1/media/uploads",
    headers={"Authorization": f"Bearer {os.environ['VIDMOAT_KEY']}"},
    files={"file": open("clip.mp4", "rb")},
    timeout=60,
)
res.raise_for_status()
print(res.json())
```

## Example response

```json
{
  "media": {
    "url": "https://api.vidmoat.com/uploads/1727950000000-clip.mp4",
    "filename": "1727950000000-clip.mp4",
    "bytes": 10485760,
    "deduplicated": false,
    "probe": { "width": 1080, "height": 1920, "fps": 30, "duration": 8.2, "hasAudio": true }
  },
  "hint": "Pass `url` as `src` to an addClip command on POST /v1/projects/{id}/commands."
}
```

---

Source: https://developer.vidmoat.com/developer/docs/api/media/upload-media
Previous: [List media](https://developer.vidmoat.com/developer/docs/api/media/list-media.md)
Next: [Import from a URL](https://developer.vidmoat.com/developer/docs/api/media/import-media.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
