What You Can Build
Media Management : Upload, organize, and manage content libraries
Content Embedding : Display OnlyFans media on your platform
Backup Tools : Download and archive creator content
Cross-Platform Sync : Manage media across multiple accounts
Quick Example
List media from the vault:
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/media" , {
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123"
}
})
const { list , hasMore } = await response . json ()
console . log ( `Found ${ list . length } media items` )
Common Operations
Get media from the vault with optional filters:
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/media?" + new URLSearchParams ({
limit: "24" ,
offset: "0" ,
sortBy: "recent" , // "recent" | "most-liked" | "highest-tips"
sortDirection: "desc" , // "asc" | "desc"
mediaType: "photo" // "photo" | "video" | "audio" | "gif"
}), {
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123"
}
})
const { list , hasMore } = await response . json ()
Get Vault Lists (Folders)
OnlyFans organizes vault media into lists/folders:
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/lists" , {
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123"
}
})
const { list , all , hasMore } = await response . json ()
// list: array of vault folders
// all: summary with total counts by type
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/lists/LIST_ID/media" , {
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123"
}
})
const { list , hasMore } = await response . json ()
Create a Vault List
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/lists" , {
method: "POST" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
name: "My Custom Folder"
})
})
const newList = await response . json ()
const response = await fetch ( "https://api.ofauth.com/v2/access/vault/lists/LIST_ID/media" , {
method: "POST" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
mediaIds: [ 123 , 456 , 789 ]
})
})
The /uploads/init response determines whether you need single-part or multi-part upload:
Header Meaning x-ofauth-upload-total-parts: 1Single-part upload (auto-completes) x-ofauth-upload-total-parts: NMulti-part upload (requires /complete)
Single-Part Upload (Small Files)
For small files where x-ofauth-upload-total-parts is 1, upload completes automatically:
// Step 1: Initialize
const initResponse = await fetch ( "https://api.ofauth.com/v2/access/uploads/init" , {
method: "POST" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
filename: "photo.jpg" ,
filesize: 1024000 ,
mimeType: "image/jpeg"
})
})
const { mediaUploadId } = await initResponse . json ()
const totalParts = initResponse . headers . get ( "x-ofauth-upload-total-parts" )
// Step 2: Upload file (auto-completes for single-part)
const uploadResponse = await fetch (
`https://api.ofauth.com/v2/access/uploads/ ${ mediaUploadId } ` ,
{
method: "PUT" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "image/jpeg"
},
body: fileBuffer
}
)
// Done! Response contains the media info directly
const { media } = await uploadResponse . json ()
console . log ( "Uploaded media ID:" , media . id )
Multi-Part Upload (Large Files)
For larger files where x-ofauth-upload-total-parts is greater than 1, upload in chunks and call /complete:
// Step 1: Initialize
const initResponse = await fetch ( "https://api.ofauth.com/v2/access/uploads/init" , {
method: "POST" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({
filename: "video.mp4" ,
filesize: 50000000 , // 50MB
mimeType: "video/mp4"
})
})
const { mediaUploadId } = await initResponse . json ()
const totalParts = parseInt ( initResponse . headers . get ( "x-ofauth-upload-total-parts" ))
const partSize = parseInt ( initResponse . headers . get ( "x-ofauth-upload-part-size" ))
// Step 2: Upload each chunk
for ( let partNumber = 1 ; partNumber <= totalParts ; partNumber ++ ) {
const start = ( partNumber - 1 ) * partSize
const end = Math . min ( start + partSize , fileBuffer . length )
const chunk = fileBuffer . slice ( start , end )
await fetch (
`https://api.ofauth.com/v2/access/uploads/ ${ mediaUploadId } /parts/ ${ partNumber } ` ,
{
method: "PUT" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "video/mp4"
},
body: chunk
}
)
}
// Step 3: Complete upload (required for multi-part only)
const completeResponse = await fetch ( "https://api.ofauth.com/v2/access/uploads/complete" , {
method: "POST" ,
headers: {
apikey: "YOUR_API_KEY" ,
"x-connection-id" : "conn_abc123" ,
"Content-Type" : "application/json"
},
body: JSON . stringify ({ mediaUploadId })
})
const { media } = await completeResponse . json ()
console . log ( "Uploaded media ID:" , media . id )
Single-part : No /complete call needed—the PUT response returns the media directly.
Multi-part : Must call /complete after all chunks are uploaded.
OFAuth provides two complementary solutions for handling OnlyFans media:
Feature Media Proxy Vault Cache Purpose Display current content Build media libraries Access Via API response URLs By media ID directly Storage Temporary (edge cache) Persistent storage Setup Automatic Requires enabling Billing Per KB bandwidth Per GB/month storage Best for Immediate display PPV features, offline access
Media Storage Guide Learn when to use Media Proxy vs Vault Cache with detailed examples
OFAuth automatically proxies OnlyFans CDN URLs, making media embeddable on your platform.
Automatic : API responses already contain proxied media.ofauth.com URLs. No extra configuration needed.
OnlyFans CDN OFAuth Media Proxy URLs expire quickly Extended validity CORS blocked CORS enabled No caching control Edge-cached globally Cannot embed Embeddable in <img> / <video>
Using Proxied URLs
// API responses automatically include proxied URLs in files.full.url
const media = list [ 0 ]
// Use directly in HTML
const imgUrl = media . files . full . url
// https://media.ofauth.com/v1/abc123/photo.jpg
<!-- Embed in your app -->
< img src = "https://media.ofauth.com/v1/abc123/photo.jpg" alt = "Content" />
< video src = "https://media.ofauth.com/v1/xyz789/video.mp4" controls / >
Trigger Persistent Caching
You can trigger persistent caching of a specific media item by sending a POST request to its media proxy URL:
await fetch ( "https://media.ofauth.com/abc123..." , { method: "POST" })
// Response: { "success": true, "mediaId": "123456", "status": "queued" }
Remove a specific media item from persistent cache with a DELETE request:
await fetch ( "https://media.ofauth.com/abc123..." , { method: "DELETE" })
// Response: { "success": true, "mediaId": "123456", "freedBytes": 1048576 }
Billing
Media Proxy Billed per KB of bandwidth transferred. Cache hits (within 7 days) are free.
Vault Cache Billed per GB/month of storage. Persistent storage for media you need long-term access to.
Monitor usage in your dashboard . Media Proxy is best for temporary display; Vault Cache is best for building permanent media libraries.
API Endpoints
Endpoint Method Description /v2/access/vault/mediaGET List all vault media /v2/access/vault/listsGET Get vault lists/folders /v2/access/vault/listsPOST Create a vault list /v2/access/vault/lists/{listId}PATCH Update a vault list /v2/access/vault/lists/{listId}DELETE Delete a vault list /v2/access/vault/lists/{listId}/mediaGET Get media in a list /v2/access/vault/lists/{listId}/mediaPOST Add media to a list /v2/access/uploads/initPOST Initialize media upload /v2/access/uploads/{mediaUploadId}PUT Upload single-part file /v2/access/uploads/{mediaUploadId}/parts/{partNumber}PUT Upload chunk /v2/access/uploads/completePOST Complete upload
Full API Reference See complete endpoint documentation
Query Parameters
Parameter Type Default Description limitnumber 24 Results per page (1-40) offsetnumber 0 Pagination offset sortBystring ”recent” Sort by: recent, most-liked, highest-tips sortDirectionstring ”desc” Sort direction: asc, desc mediaTypestring - Filter by type: photo, video, audio, gif querystring - Search query
Each media item includes:
{
"id" : 123456 ,
"type" : "photo" ,
"canView" : true ,
"hasError" : false ,
"isReady" : true ,
"createdAt" : "2024-01-15T10:30:00Z" ,
"files" : {
"full" : {
"url" : "https://media.ofauth.com/v1/abc123/photo.jpg" ,
"width" : 1920 ,
"height" : 1080 ,
"size" : 245000
},
"thumb" : {
"url" : "https://media.ofauth.com/v1/abc123/thumb.jpg" ,
"width" : 200 ,
"height" : 200
},
"preview" : {
"url" : "https://media.ofauth.com/v1/abc123/preview.jpg" ,
"width" : 480 ,
"height" : 480
}
},
"counters" : {
"likesCount" : 42 ,
"tipsSumm" : 25.00
}
}
For videos, additional fields:
{
"type" : "video" ,
"duration" : 120 ,
"videoSources" : {
"240" : "https://media.ofauth.com/v1/xyz789/240p.mp4" ,
"720" : "https://media.ofauth.com/v1/xyz789/720p.mp4"
}
}
Vault List Structure
{
"id" : 12345 ,
"type" : "custom" ,
"name" : "My Photos" ,
"hasMedia" : true ,
"canUpdate" : true ,
"canDelete" : true ,
"medias" : [
{ "type" : "photo" , "url" : "https://..." }
]
}
List types: custom, messages, posts, stories, streams, media_stickers
Tips & Best Practices
Upload First : Always upload media to the vault before referencing it in posts or messages. You can’t upload inline.
Supported Formats : Images (JPEG, PNG, GIF, WebP), Videos (MP4, MOV), Audio (MP3, M4A). Check OnlyFans’ guidelines for size limits.
Bandwidth Costs : Frequently accessed media (profile pictures, popular posts) may generate significant bandwidth charges. Monitor usage in your dashboard.