API docs / Media

Core Capabilities

Media

Upload photos and videos to OnlyFans programmatically with the OFManager API. One call per upload, deduplication built in, ready to attach to messages and posts.

Attach a photo or video to a message or post in a single call. You pass in a source, you get back a reference. The SDK handles deduplication and processing end-to-end.

Setup

import { OfApiClient } from "@betterfans/link-sdk"
import { media } from "@betterfans/link-sdk/plugins/media"

const client = new OfApiClient({
  apiKey: process.env.OFM_API_KEY,
  plugins: [media()],
})

const account = client.for("123456789")

Upload and attach

const result = await account.plugin.media.upload({
  source: "https://cdn.example/photo.jpg",
})

await account.request("POST /chats/:id/messages", {
  pathParams: { id: "98765" },
  body: {
    text: "here you go",
    mediaFiles: result.kind === "existing" ? [result.mediaId] : [result.ref],
  },
})

source accepts any of:

  • a URL string (fetched on your behalf),
  • a Blob,
  • an ArrayBuffer or Uint8Array.

The result discriminates on kind:

  • existing: a byte-identical file already lived in the account's library; mediaId is ready to attach.
  • new: a freshly-processed upload; attach the whole ref object as a single mediaFiles entry.

Mixed attachments

mediaFiles accepts a mix of both shapes in one request:

const a = await account.plugin.media.upload({ source: videoUrl })
const b = await account.plugin.media.upload({ source: photoUrl, avoidWatermark: true })

await account.request("POST /posts", {
  body: {
    text: "new drop",
    mediaFiles: [
      ...(a.kind === "existing" ? [a.mediaId] : [a.ref]),
      ...(b.kind === "existing" ? [b.mediaId] : [b.ref]),
      11111, // existing vault id you already know about
    ],
    price: 15,
  },
})

Options

OptionTypeDescription
sourcestring | Blob | ArrayBuffer | Uint8ArrayURL, Blob, or raw bytes to upload.
filenamestring (optional)Display name. Inferred from the URL when omitted.
contentTypestring (optional)MIME type. Inferred from the source when omitted.
avoidWatermarkboolean (optional)Skip watermarking (if the account has it enabled).

Result

OptionTypeDescription
kind"existing" | "new"existing for a library hit, new for a fresh upload.
mediaIdnumberLibrary id (only when kind === "existing"). Attach directly.
refMediaRefOpaque reference (only when kind === "new"). Attach as a single mediaFiles entry.

Refreshing cached account state

The plugin caches a small amount of per-account state to keep repeat uploads fast. If an account's profile settings change out of band and you want the next upload to pick them up immediately rather than waiting for the internal TTL:

client.plugin.media.invalidateCache("123456789")