Downloading Media
Learn how to download photos/videos/audio from OnlyFans and Fansly CDN URLs and store them locally or in your cloud storage.
OnlyFans API makes it easy to download media files directly from OnlyFans and Fansly CDN URLs. You can download photos, videos, and audio files by calling our download endpoint with the CDN URL.
Each platform has its own endpoint: OnlyFans below, or jump to Fansly.
Download media from OnlyFans CDN
The full media download endpoint documentation can be found here.
Media can be downloaded by calling GET https://app.onlyfansapi.com/api/{account}/media/download/{ONLYFANS_CDN_URL}.
The {ONLYFANS_CDN_URL} parameter should be a URL from any OnlyFans CDN subdomain (i.e. anything matching https://cdn*.onlyfans.com/*, such as cdn2.onlyfans.com, cdn3.onlyfans.com, etc.). A typical URL looks like https://cdn2.onlyfans.com/files/e/e5/123/600x400_123.jpg?Tag=2&u=123&Policy=123&Signature=signature&Key-Pair-Id=123. You can just enter the OnlyFans CDN URL after the .../media/download/{ONLYFANS_CDN_URL} without any need for encoding the URL.
curl --location 'https://app.onlyfansapi.com/api/{account}/media/download/https://cdn2.onlyfans.com/files/e/e5/123/600x400_123.jpg?Tag=2&u=123&Policy=123&Signature=signature&Key-Pair-Id=123' \
--header 'Authorization: Bearer {token}' \
--output 'downloaded-media.jpg'The endpoint returns a 302 redirect to a cached file on cdn.fansapi.com, or to dl.fansapi.com to stream an uncached file from OnlyFans. Follow the redirect to receive the binary response with its Content-Type (e.g. image/jpeg, video/mp4, audio/mpeg). Downloads from our CDN cache are free.
Detecting file type
Signed CDN URLs often carry query parameters (e.g. ?Tag=…&Signature=…) and may not have a clean file extension. Inspect the response's Content-Type header to determine how to handle the file rather than parsing the URL.
Request part of a file with Range
For non-DRM media, send a Range header to request only the bytes you need. This can reduce transfer time when previewing a video, seeking, or resuming an interrupted download.
This example downloads the first 1 MB (1,000,000 bytes):
curl --location \
'https://app.onlyfansapi.com/api/{account}/media/download/{ONLYFANS_CDN_URL}' \
--header 'Authorization: Bearer {token}' \
--header 'Range: bytes=0-999999' \
--dump-header 'response-headers.txt' \
--output 'video-first-mb.bin'Replace {ONLYFANS_CDN_URL} with the complete CDN URL, including all signed query parameters. Keep the URL quoted. curl --location forwards Range across redirects without forwarding your bearer token to a different host; do not add --location-trusted.
- Byte offsets are zero-based and inclusive.
bytes=1000000-1999999requests the second MB,bytes=1000000-requests the remaining bytes from that offset, andbytes=-1000000requests the last MB. - Check the final response after redirects: a supported range returns
206 Partial Content.Content-Rangeidentifies the returned slice and full file size, whileContent-Lengthis the size of the slice. - If the upstream server ignores the range, the response is
200 OKand can contain the full file. An unsatisfiable range returns416 Range Not Satisfiable. - Credits are based on the bytes streamed for the requested chunk, rather than the size of the whole file. Each non-empty HTTP GET transfer has a 1-credit minimum, including each Range request. Repeated or overlapping chunks consume credits again. See media download pricing.
For repeated playback, download once and cache the file in your own storage, or use an available cdn.fansapi.com URL. Range requests reduce unnecessary bytes; downloading every byte in multiple requests does not inherently make a full download faster.
Read file metadata with HEAD
Use HEAD on the same non-DRM endpoint to retrieve headers without a response body:
curl --head --location \
'https://app.onlyfansapi.com/api/{account}/media/download/{ONLYFANS_CDN_URL}' \
--header 'Authorization: Bearer {token}'The final response includes the equivalent GET's metadata, such as Content-Type and, when available, the full file's Content-Length. HEAD consumes no download credits and does not start a background cache download. Range applies to GET requests; omit it from HEAD.
Downloading DRM-protected videos
Some Media Vault videos have DRM (Widevine) enabled. These return files.full.url = null from the Vault endpoints and have no plain CDN URL to pass to the endpoint above, so they use a separate endpoint that takes the vault media ID (data.id) instead of a CDN URL:
Call GET https://app.onlyfansapi.com/api/{account}/media/download/drm/{media_id}. It resolves the Widevine license, decrypts the video and audio, and returns a standard MP4.
curl --location 'https://app.onlyfansapi.com/api/{account}/media/download/drm/2381923812' \
--header 'Authorization: Bearer {token}' \
--output 'downloaded-video.mp4'A few things differ from the regular download endpoint above:
- The response is a
302redirect todl.fansapi.com, which streams the decrypted file. Your client must follow redirects (curlneeds--location). - Expect roughly 8 to 15 seconds before the first byte arrives while the license exchange and decrypt run. Repeat downloads of the same video are faster, because the content keys are cached.
- Downloads cost 3 credits per decimal MB, with a minimum of 1 credit for a non-empty download. Billing uses the decrypted, remuxed MP4 bytes streamed, including bytes streamed before a disconnect or error. No output bytes means no download charge. See the exact credit calculation.
- Cached content keys speed up preparation; they do not make another DRM download free. Download once and cache the MP4 in your own storage for repeated playback.
- DRM downloads do not support
Range: the service returns a200 OKstream withAccept-Ranges: none, even when a Range header is sent. It cannot provideContent-Lengthin advance.HEADis not supported and returns405 Method Not Allowed.
Which videos are DRM-protected?
A video is DRM-protected when its Media Vault item returns files.full.url = null and a files.drm block. Non-DRM media keeps working through the regular Download Media from the OnlyFans CDN endpoint. See Download DRM-protected Media for the full reference.
Downloading media from the Fansly CDN
Fansly media works the same way in spirit, but through a separate endpoint that takes a fansly_acct_… ID:
Call GET https://app.onlyfansapi.com/api/fansly/{fanslyAccount}/media/download/{FANSLY_CDN_URL}.
The {FANSLY_CDN_URL} parameter should be a URL from any Fansly CDN subdomain (anything matching https://cdn*.fansly.com/*, such as cdn3.fansly.com). As with OnlyFans, paste the URL straight after .../media/download/ — no encoding needed.
curl --location 'https://app.onlyfansapi.com/api/fansly/{fanslyAccount}/media/download/https://cdn3.fansly.com/100000000000000001/300000000000000001.jpeg?ngsw-bypass=true&Expires=1782917529&Key-Pair-Id=K23PG5J1AWEZX5&Signature=signature' \
--header 'Authorization: Bearer {token}' \
--output 'downloaded-media.jpg'Two differences from the OnlyFans endpoint are worth knowing before you build against it:
- It always returns a
302redirect, never a binary body. If we already have the file cached it redirects to a signedcdn.fansapi.comURL; if not, it redirects to the original Fansly URL and queues the file for caching in the background, so the next call is served from our edge. Your client must follow redirects (curlneeds--location). - The signed query parameters are required. Fansly CDN URLs carry
Expires,Key-Pair-IdandSignature. Strip any of them and the endpoint returns a422telling you which are missing, because we cannot fetch the file to cache it.
Fansly URLs in API responses may already point at our CDN
Endpoints like List Chat Messages and Get Account Media rewrite media URLs we have already cached to short-lived cdn.fansapi.com URLs. Those are fetchable directly — no need to route them back through the download endpoint (though doing so is harmless; it just redirects them straight back). Uncached URLs are returned as the original cdn*.fansly.com URL. See Endpoint Data Sources for the full picture.
Fansly does not use DRM
There is no Fansly equivalent of the OnlyFans DRM download endpoint, and none is needed — Fansly video is not Widevine-protected. Every Fansly media file goes through the endpoint above.
Streaming manifests (.m3u8 / .mpd) are the one thing we redirect but never cache: a manifest is a playlist of separately-signed segment URLs rather than a media file, so caching it would serve you a list of links that no longer work.
🚀 Bulk downloads: export your entire Media Vault
Downloading the full Media Vault one CDN URL at a time is slow and rate-limited. For bulk use cases, use our Data Exports feature instead, which packages your entire Media Vault (every photo, video, and audio file) into a single ZIP file you can download once it's ready.
Data Exports currently covers OnlyFans accounts only. For Fansly, page through Get Account Media and download each URL individually.
This is the recommended approach for:
- AI / LoRA training datasets: Get every asset in one archive instead of stitching together thousands of individual downloads. See Download Media and Chats for LLM and LoRA Training for a worked example.
- Backups and platform migration: Snapshot the full vault for disaster recovery or before switching platforms. See Complete Data Backup and Migration.
- Bulk content repurposing: Pull your full media library at original quality for use elsewhere.
You can kick off a Media export from the Data Exports dashboard (no code) or programmatically via the Create Data Export API. Big vaults may take several hours to package; you can close the browser and come back later to download the result.
Migrating from the deprecated scrape endpoint
The previous POST /api/{account}/media/scrape endpoint is deprecated. If you have an existing integration built against it, here's what changes when you switch to the new download endpoint:
- Response shape: The deprecated endpoint re-hosted the file on the OnlyFans API CDN and returned a URL. The new endpoint streams the file binary directly in the response, so you'll need to save the bytes (not a URL) on your side.
- File size limit: The new endpoint has no file size limit. The deprecated endpoint capped uploads at 500MB.
- Input format: The new endpoint accepts a CDN URL only. Vault Media IDs (which the deprecated endpoint accepted) are not supported here.