---
title: "Upload media from a URL"
method: POST
path: "/media/upload-from-url"
tags: ["Media"]
---

# Upload media from a URL

`POST /media/upload-from-url`

Download a file from a URL and upload it to your media library — the recommended path for MCP/programmatic uploads. Supports videos up to **1 GB**: files over 100 MB are streamed to storage by a background worker and the response returns `202` with the item in `processing` state (poll `GET /media/{id}` until `status` is `ready`). Files at or under 100 MB upload inline and return `201` ready. A PDF URL is split into image slides and returned as a carousel (see PdfUploadResult; pass all of `media_ids` to POST /posts). Every response also includes a `compatibility` block listing any connected platforms that would reject the file by size or format.

## Request body

- object
  - `url` string, uri, required — HTTP or HTTPS URL of the file to download (max 1 GB for video; per-platform caps apply at scheduling).
  - `filename` string — Optional filename override
  - `name` string — Optional human-readable label so the asset is findable by name later (e.g. "pp-play5get50").
  - `folder` string — Optional folder name to file the asset under (created at the top level if missing).
  - `folder_id` string — Optional id of an existing folder to file the asset under.

## Response `201`

File uploaded inline (<= 100 MB) and ready to use. A single `data` (Media) for image/video; a PdfUploadResult (`slides` + `media_ids`) for a PDF.

- union
  - object
    - `data` Media
      - `id` string — Media ID (use this in post creation)
      - `url` string — Permanent CDN URL for the media
      - `thumbnail_url` string, nullable — CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video'
      - `name` string, nullable — Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable — Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string — Generated storage filename (stable, used internally)
      - `size` string — Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed' — Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing' — poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
    - `compatibility` MediaCompatibility — Whether the file fits the platforms connected to your workspace. `compatible` is true when no connected platform would reject it.
      - `compatible` boolean
      - `warnings` object[]
        - `platform` string
        - `display_name` string
        - `reasons` string[] — e.g. ["max 300 MB"], ["format not supported"]
      - `summary` string, nullable — One-line human summary, or null when fully compatible.
    - `message` string
  - PdfUploadResult — Returned when the uploaded file is a PDF. A PDF only renders natively on LinkedIn, so the server rasterizes each page into an image slide and stores each as its own media item. `data` mirrors the FIRST slide (so callers that read a single Media still work); `slides` and `media_ids` carry the whole carousel in page order. Pass ALL of `media_ids` to POST /posts as a carousel. On LinkedIn it posts as a swipeable document; on Instagram, TikTok, Threads and Pinterest as an image carousel. Capped at 20 pages.
    - `data` Media
      - `id` string — Media ID (use this in post creation)
      - `url` string — Permanent CDN URL for the media
      - `thumbnail_url` string, nullable — CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video'
      - `name` string, nullable — Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable — Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string — Generated storage filename (stable, used internally)
      - `size` string — Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed' — Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing' — poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
    - `slides` Media[] — Every page as a Media item, in page order.
      - `id` string — Media ID (use this in post creation)
      - `url` string — Permanent CDN URL for the media
      - `thumbnail_url` string, nullable — CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video'
      - `name` string, nullable — Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable — Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string — Generated storage filename (stable, used internally)
      - `size` string — Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed' — Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing' — poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
    - `media_ids` string[] — Slide ids in page order — pass all of them to POST /posts.
    - `pdf` object
      - `total_pages` integer
      - `rendered_pages` integer
      - `truncated` boolean — True when the PDF had more than 20 pages and only the first 20 were imported.
    - `compatibility` MediaCompatibility — Whether the file fits the platforms connected to your workspace. `compatible` is true when no connected platform would reject it.
      - `compatible` boolean
      - `warnings` object[]
        - `platform` string
        - `display_name` string
        - `reasons` string[] — e.g. ["max 300 MB"], ["format not supported"]
      - `summary` string, nullable — One-line human summary, or null when fully compatible.
    - `message` string

## Other responses

- `202` — Large file (over 100 MB) accepted and processing. `data.status` is 'processing' — poll GET /media/{id} until 'ready' before using it.
- `400` — Invalid URL or unsupported file type
- `413` — File exceeds the 1 GB limit

---

[API](https://skmtc.dev/omnisocials/apis/omnisocials-api.md) · [All operations](https://skmtc.dev/omnisocials/apis/omnisocials-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/omnisocials/omnisocials-api/revisions/e53b89af0b98/schema)
