API Docs/Reference/Upload Media
API Reference

Upload Media

Re-host an image, video, or PDF for use in a post. Provide either url or file_base64, not both. Requires a paid Creator or Lifetime plan.

POSThttps://api.linkedmash.com/v1/media/upload

POST /v1/media/upload re-hosts an image, video or PDF on LinkedMash and returns a stable public URL to use as media_url on a draft or a schedule call. It takes either a remote url to fetch and copy, or file_base64 with a file_name for raw bytes — one or the other, not both.

Re-hosting is the recommended path even when you already have a URL. A scheduled post publishes hours or days after it is created, and an asset that has expired or moved by then publishes as a post with a broken attachment.

Example request

Authenticate with a bearer token in the Authorization header. Create a key on the API page in your LinkedMash settings and export it as LINKEDMASH_API_KEY before running this.

curl
curl -X POST "https://api.linkedmash.com/v1/media/upload" \
  -H "Authorization: Bearer $LINKEDMASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/assets/diagram.png",
    "mime_type": "image/png"
  }'

Header parameters

AuthorizationstringRequired

Your secret API key as a Bearer token.

Body parameters

urlstringOptional

Remote http(s) URL to re-host. Provide either url or file_base64.

file_base64stringOptional

Base64-encoded file bytes — an alternative to url.

file_namestringOptional

Original file name.

mime_typestringOptional

MIME type of the file (e.g., 'image/png').

Response

{
  "status": true,
  "data": {
    "url": "https://media.linkedmash.com/uploads/9c1f0b2e/diagram.png",
    "file_type": "image",
    "file_name": "diagram.png",
    "mime_type": "image/png"
  }
}

Response fields

Every response is wrapped in the same envelope: a status flag and a data payload, with meta present where there is pagination or a plan limit to report.

statusboolean

true on success. Every LinkedMash API response carries this envelope flag alongside data, so a client can branch on it without re-reading the HTTP status.

data.urlstring

The re-hosted, stable media URL. This is the value to pass as media_url when creating, updating or scheduling a post — not the source URL you uploaded from.

data.file_typestring

Resolved category: image, video or document.

data.file_namestring

Stored filename, taken from the source URL or from the file_name you sent with file_base64.

data.mime_typestring

Stored MIME type. Defaults to application/octet-stream when a base64 upload arrives without one.

Attaching media to a post

Three calls, and the first one is the one people skip.

1

Re-host the asset

Upload it here and keep data.url. Passing a source URL straight to a draft works right up until the source expires, and a scheduled post publishes long after you created it.

2

Attach it to the draft

Create or update the draft with media_url set to data.url, together with post_type and media_mime_type. The upload alone does not attach anything, even when you passed draft_id.

3

Verify before scheduling

GET /v1/posts/{id} and confirm the media is on the draft. Catching a missing attachment now is cheaper than catching it in a published post.

4

Prefer base64 for private assets

The url path requires a publicly fetchable address. Anything behind auth has to go through file_base64 with an explicit file_name.

Behaviour and limits

Requires a Creator or Lifetime plan

Everything under /v1/posts, /v1/queue, /v1/schedule, /v1/media and /v1/analytics sits behind one middleware that checks for an unlocked Studio, so a Reader-plan or free key gets 402 with code SUBSCRIPTION_REQUIRED before the handler runs. Bookmark and label reads are on a separate router and are not affected by this gate.

Returns 201 with the URL you actually use

data.url is the re-hosted address; data.file_type, data.file_name and data.mime_type describe what was stored. Pass data.url as media_url, not the source URL you sent.

Only http and https sources are fetched

A url with any other scheme is rejected with 400 "Only http(s) urls are supported." A source that responds non-2xx is also a 400, carrying the upstream status, rather than a 502.

file_base64 requires file_name

Raw-byte uploads have no filename to infer, so file_name is mandatory on that path. mime_type falls back to application/octet-stream if you omit it, which is rarely what you want for an image.

draft_id is optional and only associates the asset

Passing draft_id ties the upload to a draft for housekeeping. It does not attach the media to the post — that still needs media_url on a create or update call.

Errors

Failures carry the same envelope as a success, with the reason in message and a stable machine-readable code where the status alone is ambiguous — several distinct conditions share a 402.

Status
Meaning
400Bad Request
Neither url nor file_base64 was given, the url was invalid or not http(s), the source could not be fetched, file_name was missing alongside file_base64, or the base64 payload could not be decoded.
401Unauthorized
The Authorization header is missing or the key is not a valid LinkedMash API key. Create or rotate keys on the API page in your LinkedMash settings.
402Payment Required
Returned with code SUBSCRIPTION_REQUIRED. The content API requires a Creator or Lifetime plan; Reader and free accounts get this on every /v1/posts, /v1/queue, /v1/schedule, /v1/media and /v1/analytics call.
500Server Error
Something failed on our side. The body carries message "Failed to upload media". Retry once; if it persists the request is not the problem.