The Only API docs
Content

Upload media to the vault

Upload a photo, video, gif or audio file to the account's vault, and get back a reference you can attach to a post, message or story.

POST
/accounts/{of_user_id}/media

Upload a photo, video, gif or audio file to the account's vault, and get back a reference you can attach to a post, message or story.

Two ways to call it:

  1. multipart/form-data with a file part — raw bytes.
  2. application/json with {"source_url": "https://…"} — we fetch the file server-side. Use this when your media already lives somewhere public (Drive, S3, a CDN); it saves you building a multipart request at all.

What happens under the hood. OnlyFans has no single upload endpoint. The web client runs a four-stage pipeline and so do we: POST /api2/v2/upload/signed/createPUT the bytes straight to the returned pre-signed S3 URL (5 MiB parts for files ≥ 5 MiB) → POST /api2/v2/upload/signed/finish → hand the S3 descriptor to OnlyFans' converter host. You do not have to orchestrate any of that.

Using the result. Put the returned media object into the post/message's mediaFiles array — not media, which OnlyFans silently ignores for freshly uploaded files. Pass the object through whole; trimming it to just processId also silently attaches nothing:

{ "text": "new set 🔥",  "mediaFiles": [ { "processId": "0ifuov…", "host": "convert4.onlyfans.com", "thumbId": 1, "name": "IMG_2676.HEIC", "extra": "…" } ],  "isScheduled": 1, "scheduledDate": "2026-08-20T12:00:00+00:00" }

⚠️ Two things that fail silently, both confirmed on a live account:

  • Using media instead of mediaFiles, or trimming the object down to {processId}, returns 200 with no media attached.
  • postedAt does not schedule anything. In either format (…Z or …+00:00) OnlyFans ignores it and publishes immediately — verified live. Scheduling requires two fields instead:
{ "isScheduled": 1, "scheduledDate": "2026-08-20T12:00:00+00:00" }

Always confirm the post actually queued by checking GET /api2/v2/schedules for its id — a post that published instead of queueing looks identical in the create response apart from postedAt.

Media already in the vault is still referenced by plain integer id — list those with GET /api2/v2/vault/media.

An upload does not by itself create a vault item, and that matches OnlyFans exactly. OnlyFans has no upload-to-vault endpoint: its own vault page is browse/organise only (list, hide, folders, attach), and the uploader is wired solely into the post/message composer. The vault entry is created when a post, message or story consumes the processId. So immediately after this call the file will not appear in GET /api2/v2/vault/media, nor in GET /api2/v2/vault/media/processing, nor under GET /api2/v2/vault/media/hash?h={md5}&size={bytes} — that last one is the lookup OnlyFans' own client uses to decide whether a file is already in the vault, and it answers 404 Media Not Found. Publish or schedule with the processId and the vault row appears. This is the same behaviour you get clicking upload in the OnlyFans web app.

Working example: a runnable Python client covering upload, immediate posting and scheduling — including every silent-failure case above — is at theonlyapi-posting-example.zip.

Requires writes to be enabled for the account (PATCH /accounts/{of_user_id}/polling {"allow_of_write_actions": true}), because uploading acts as the creator.

OnlyFans only. The Fansly upload pipeline is not wired yet; a Fansly account returns 501.

(This replaces the long-documented POST /api2/v2/media, which never existed.)

X-API-Key<token>

Your CRM panel API key

In: header

Path Parameters

of_user_id*string

Creator account ID of the connected account (OnlyFans or Fansly). Use GET /accounts to list connected accounts and their IDs.

Header Parameters

X-Proxy?string

Proxy URL — HTTP or SOCKS5 (e.g. http://user:pass@host:port or socks5://user:pass@host:port). Optional on post-connection routes, where it overrides the proxy saved at login time. Required on the connection routes (POST /accounts/login, POST /accounts/login/cookies, POST /accounts/login/verify-otp) for OnlyFans accounts; optional for Fansly.

Request Body

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/accounts/string/media" \  -H "Content-Type: application/json" \  -d '{    "source_url": "http://example.com"  }'
{  "success": true,  "media": {    "processId": "string",    "host": "string",    "name": "string",    "extra": "string"  },  "data": {}}