# Upload media

> Images and thumbnails in one PUT, videos in parts, then attach them to posts.

Media goes straight to storage: the API gives you upload addresses, you send the bytes there, then you tell the API the upload is complete. Files count toward your plan's storage.

## Images and thumbnails

```bash
curl https://api.nowscale.com/v1/media \
  -H "Authorization: Bearer $NOWSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-cover-42" \
  -d '{ "project_id": "PROJECT_ID", "kind": "image", "content_type": "image/png", "size_bytes": 482113 }'
```

```json
{ "object": "media_upload", "media_id": "MEDIA_ID", "method": "single", "upload_url": "https://...", "part_size": null, "part_count": null, "content_type": "image/png" }
```

Send the file to `upload_url` with a `PUT` and the same `Content-Type`, then complete it:

```bash
curl -X PUT "$UPLOAD_URL" -H "Content-Type: image/png" --data-binary @cover.png
curl -X POST https://api.nowscale.com/v1/media/MEDIA_ID/complete \
  -H "Authorization: Bearer $NOWSCALE_API_KEY" -H "Content-Type: application/json" -d '{}'
```

## Videos

Videos upload in parts, so a large file can resume after a network error.

1. `POST /v1/media` with `"kind": "video"` returns `part_size` and `part_count`.
2. `POST /v1/media/{media_id}/parts` with `{ "part_numbers": [1, 2, 3] }` returns a short-lived address per part.
3. `PUT` each part (bytes `(n-1) × part_size` to `n × part_size`) and keep the `ETag` header of each response.
4. `POST /v1/media/{media_id}/complete` with every part and its ETag:

```json
{
  "parts": [{ "part_number": 1, "etag": "\"a54357aff0632cce46d942af68356b38\"" }, { "part_number": 2, "etag": "\"0c78aef83f66abc1fa1e8477f296d394\"" }],
  "duration_seconds": 42.5,
  "width": 1080,
  "height": 1920
}
```

Duration and size are optional; each network checks the real file again at publish time.

## Limits

`GET /v1/networks` lists, per network, the media kinds a post can carry, the accepted file types and the largest file. Your plan also has a per-file size and a storage total; an upload over them answers with codes such as `plan_limit_video_size`.

Media is ready when its `status` is `uploaded`. Attach it to a post with `media_ids`.
