# Uploads and media

> How to give a model a picture, a clip or a voice. Public links work directly; a local file goes up in two steps.

## Two ways in

1. **A public link.** Put it straight in `image_urls`, `audio_urls` or `video_url`. It must open without a login and answer a ranged request with a size.
2. **A local file.** Ask for a signed upload, then send the bytes.

An `asset://` link is refused (`REFERENCE_FORBIDDEN`). Our own media links work only if the file is yours or a teammate's.

## Upload a local file

Step 1: say what you will send. The size must be exact.

**cURL**

```bash
curl -X POST "https://app.diffusr.ai/api/v2/uploads" \
  -H "Authorization: Bearer $DIFFUSR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "content_type": "video/mp4",
  "size_bytes": 12345678
}'
```

**TypeScript**

```ts
import { Diffusr } from "diffusr";

const d = new Diffusr(); // reads DIFFUSR_KEY
// Copies a public link in. For a local file use d.uploadFile("./clip.mp4").
const up = await d.uploadFromUrl("https://example.com/clip.mp4");
console.log(up.file_url);
```

**Python**

```python
from diffusr import Diffusr

d = Diffusr()  # reads DIFFUSR_KEY
# Copies a public link in. For a local file use d.upload_file("./clip.mp4").
up = d.upload_from_url("https://example.com/clip.mp4")
print(up["file_url"])
```

The answer has `upload_url`, `file_url` and the headers to use. Step 2: `PUT` exactly those bytes, with the same `Content-Type`, to `upload_url` within 15 minutes:

```bash
curl -X PUT -H "Content-Type: video/mp4" --data-binary @clip.mp4 "UPLOAD_URL"
```

Step 3: use `file_url` in your call. A signed upload that was never sent is marked expired after about 20 minutes. The SDKs do all three steps in `uploadFile` / `upload_file`.

If the file is already online, pass `source_url` instead and we copy it. For a large video, download it and use the signed upload.

## What each file must be

- The right kind: a picture for `image_urls`, audio for `audio_urls`, video for `video_url` (we check the file's first bytes, not just its name).
- Within limits: 25 MB a picture, 250 MB a video, 50 MB a sound, 300 MB together.
- Seedance models also need pictures up to 6000 px a side with width divided by height between 0.4 and 2.5. Some models limit clip or voice length; the length must be readable (mp4, mov, m4a, wav or mp3). `recast` reads the clip's real length and prices by it, so send mp4 or mov.
- A dry run only checks the shape. The file itself is checked when you start the real job, before anything is reserved.

Refusals are `422` (or `403` for access rules) with `code`, `field` and `index`: `REFERENCE_UNREACHABLE`, `REFERENCE_TOO_LARGE`, `REFERENCE_UNSUPPORTED`, `REFERENCE_FORBIDDEN`.

## Quotas and how long files stay

| | Live keys | Test keys |
| --- | --- | --- |
| Uploads per 24 hours, per account | 500 | 50 |
| Stored at once | 10 GB | 500 MB |
| Kept after the last time a job used the file (or after upload, if never used) | 7 days | 1 day |

Largest file: 25 MB for a picture, 250 MB for a video, 50 MB for a sound.

Over the count: `429 UPLOAD_QUOTA`. Over the storage: `413 STORAGE_QUOTA`; uploads clear on their own. Only API uploads are cleaned up. Your results are not uploads: they stay like the app's.

See [upload_asset](https://diffusr.ai/docs/reference/upload_asset).

Source: https://diffusr.ai/docs/uploads
