---
title: "Retrieve an upload"
method: GET
path: "/uploads/{upload_id}"
tags: ["Uploads"]
---

# Retrieve an upload

`GET /uploads/{upload_id}`

## Request

- Base URL: `https://api.shortsync.app/v1`
- URL: `https://api.shortsync.app/v1/uploads/{upload_id}`
- Auth: HTTP bearer

Returns the current state of an upload. On the first call after the
client has PUT bytes to the presigned URL, ShortSync finalizes the
upload: it reads the stored `Content-Type`, validates it against
the MIME allow-list, checks per-kind size caps and workspace storage
quota, and marks the upload `ready`. Polling before the PUT is
cheap — calls just return `status: "reserved"` with no side effects.

If the stored `Content-Type` isn't supported, the server deletes
the S3 object and responds `415 UNSUPPORTED_FILE_TYPE`. Reserve a
new `upload_id` and retry with a correct `Content-Type` on the PUT.

## Response `200`

Upload.

- Upload
  - `id` string, required
  - `status` 'reserved' | 'ready', required: - `reserved` - ticket issued, bytes not yet finalized - `ready` - bytes finalized, usable as a post source In v1 the server skips the post-upload media probe, so transitional `uploaded` / `failed` states are reserved for future use.
  - `filename` string, required: Original filename if supplied to `POST /uploads`; otherwise derived from the S3 object key (typically the literal `"upload"` for empty-body reservations).
  - `content_type` string, required: Canonical MIME of the uploaded bytes (e.g., `video/mp4`, `image/jpeg`), read from the `Content-Type` the client set on the PUT to the presigned URL and validated against the allow-list at finalize.
  - `size_bytes` integer, required
  - `workspace_id` string, required
  - `media` UploadMedia: Probed properties of the uploaded file. Populated for `video` uploads via server-side ffprobe at finalize time; fields fall back to `null` when a probe field can't be read. Images currently report `content_kind: "image"` with all other fields `null`.
    - `content_kind` 'video' | 'image'
    - `duration_seconds` number, nullable
    - `width` integer, nullable
    - `height` integer, nullable
    - `aspect_ratio` number, nullable
    - `fps` number, nullable
    - `bitrate_kbps` integer, nullable
    - `video_codec` string, nullable
    - `audio_codec` string, nullable
  - `error` object, nullable
    - `code` string
    - `message` string
  - `created_at` string, date-time, required
  - `updated_at` string, date-time

## Other responses

- `401`: Missing, malformed, or revoked token.
- `403`: Token is valid but cannot access this resource. Common cases: - `INSUFFICIENT_SCOPE` - token lacks the required scope. - `WORKSPACE_NOT_ALLOWED` - account token is allowlisted to other workspaces.
- `404`: Resource does not exist or is not visible to this token.
- `413`: Upload exceeds per-kind size cap (2 GB video, 20 MB image) or workspace storage quota. The S3 object is deleted server-side before this response is returned.
- `415`: The stored `Content-Type` isn't in the allow-list (mp4, mov, webm, avi, mkv, jpg, png, webp, heic, gif). The S3 object is deleted server-side before this response is returned.
- `429`: Per-key sliding-window rate limit (60 req/min), per-org monthly request quota, or per-user upload cap was hit. `RATE_LIMIT_EXCEEDED` carries the window headers; `QUOTA_EXCEEDED` carries `details.quota` (which counter), `used`, `limit`, `resets_at`, and `upgrade_url`. `UPLOAD_QUOTA_EXCEEDED` (Free tier on `POST /posts` only) carries the same shape plus `details.remaining`.

---

[API](https://skmtc.dev/shortsync/apis/shortsync-api.md) · [All operations](https://skmtc.dev/shortsync/apis/shortsync-api/llms.txt) · [OpenAPI document](https://skmtc.dev/shortsync/apis/shortsync-api/revisions/71fdaef1e400?raw)
