# XFree Video Scraper (`datawatcher/xfree-scraper`) Actor

Scrapes public video data from xfree.com searches, profiles, tags and video pages: creator with OnlyFans link, tagged accounts, tags, views, likes, date, duration, thumbnail and the direct .mp4 video file URLs. Or just the video page and file URLs.

- **URL**: https://apify.com/datawatcher/xfree-scraper.md
- **Developed by:** [DataWatcher](https://apify.com/datawatcher) (community)
- **Categories:** Videos
- **Stats:** 3 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 video results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## XFree Video Scraper

Scrape video data from xfree.com by search, account, tag or video URL, as JSON, CSV or Excel, or through the **API**. **Every row has the direct link to the video file (`.mp4`)**: open it to play the video, or save it to download it. You also get the creator's **OnlyFans link**, title, creator, tagged accounts, views, likes, date, duration, tags and thumbnail.

- Direct video links: `videoFileUrl` (full quality) and `previewVideoUrl` (smaller copy).
- OnlyFans link: `creator.onlyfansUrl`.
- Searches, profiles and tags are read page by page until your `maxResults` or another cap in the input is reached.

The actor reads public data and returns the links to the videos.

### 🔎 What it extracts

One row per unique video. The page types it reads:

| Page type | Example URL | Input to use |
|---|---|---|
| Search | `https://www.xfree.com/search?q=examplecreator` | `searchQueries` or `startUrls` |
| Profile (account) | `https://www.xfree.com/examplecreator` | `usernames` or `startUrls` |
| Tag, including language tags | `https://www.xfree.com/tag/Solo`, `https://www.xfree.com/tag/pl-Amatorzy` | `tags` or `startUrls` |
| Video page | `https://www.xfree.com/video?id=123456789&user=examplecreator` | `videoUrls` or `startUrls` |

- Tag names are kept exactly as the site writes them (`pl-Amatorzy`).
- Search tabs: `all` (default), `post` and `tag`.
- Account data (followers, total videos, bio and more) is saved once per profile in the run stats (`accountProfiles`).

### 🚀 How to scrape XFree videos

1. Open the actor on Apify and click **Try for free**.
2. Fill in a search, usernames or tags, or paste xfree.com URLs into `startUrls` or `videoUrls`.
3. Click **Start**.
4. Download the **XFree video data** as JSON, CSV or Excel from the Output tab, or fetch it with the Apify API.

Example inputs:

```json
{ "searchQueries": [{ "query": "examplecreator", "filter": "all" }], "maxResults": 50 }
```

```json
{ "usernames": ["examplecreator"], "maxResults": 100 }
```

```json
{ "tags": ["pl-Amatorzy"], "tagSort": "latest", "maxResults": 100 }
```

### 🤖 Use with AI agents (MCP)

AI assistants (Claude, ChatGPT, Cursor…) can run this actor through Apify's MCP server. Connect the server in your assistant, then ask, for example:

```text
Use the XFree Video Scraper to get the 20 newest videos of examplecreator with their direct .mp4 links and OnlyFans link.
```

### 🔌 Run it your way

- **Schedule it** with Apify schedules.
- **Call it from the Apify API** and its clients (Python, JavaScript).
- **Connect it** to Make, n8n, Zapier, webhooks or Google Sheets.
- **Ask an AI agent to run it**: see [Use with AI agents (MCP)](#-use-with-ai-agents-mcp).

### 🔀 Mixing inputs and duplicates

You can use all five source inputs in one run: `startUrls`, `searchQueries`, `usernames`, `tags`, `videoUrls`.

| Rule | What happens |
|---|---|
| One row per video | Matched by `id` over the whole run: a video found by a search and by its profile is written once. |
| First input kept | `sourceType` and `sourceUrl` keep the first input that found it. |
| Same input twice | Read once. |
| `maxResults` | One cap for the run, split fairly over the inputs still to read. |
| Input check | Every input is checked: valid ones run, others get a clear message. |

### 🎬 Direct video links

> **`videoFileUrl` is the direct link to the video file**, e.g.
> `https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/full.mp4`.
> Open it in a browser to play the video, or save it to download it. It is the full-quality file the site's player plays.
>
> **`previewVideoUrl` is the smaller copy** the site plays in its lists, e.g.
> `https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/listing7.mp4?1`.

- Both links come with every row in every mode at no extra request. `mediaUrlsFound` and `mediaUrlsMissing` in the run stats count the rows with and without a file link.
- `url` is the video's page on xfree.com, not the file.

### 🔗 OnlyFans link

`creator.onlyfansUrl` is the creator's OnlyFans link, e.g. `https://onlyfans.com/examplecreator`. It is the link button the site shows with the video. On a profile, a video without a button gets the profile's own OnlyFans link.

### ⚡ Links-only mode (`onlyVideoUrls`)

With `onlyVideoUrls` set to `true`, each row has only the video page and the two direct links (see the links-only row under Output). It is the fastest run. The price per video is the same as for a full row.

### ⚙️ Input

| Input | Type | Default | Range | Meaning |
|---|---|---|---|---|
| `startUrls` | list of URLs | none | xfree.com URLs | Search, profile, tag or video page URLs. The kind is found from the URL. |
| `searchQueries` | list | none | – | Items `{"query": "...", "filter": "all" \| "post" \| "tag"}`. `filter` is optional (default `all`). A plain text line means `{"query": text, "filter": "all"}`. |
| `usernames` | list of text | none | – | Account handles, with or without `@`, or profile URLs. Every video of each account. |
| `tags` | list of text | none | – | Tag names exactly as the site writes them (e.g. `pl-Amatorzy`), or tag page URLs. |
| `tagSort` | text | empty (site default order) | `latest` or `popular` | Order of tag pages. |
| `videoUrls` | list of URLs | none | – | Video page URLs. One row per video. |
| `onlyVideoUrls` | yes/no | `false` | – | Rows are only `url`, `videoFileUrl`, `previewVideoUrl`. |
| `maxResults` | integer | `100` | 1 to 1,000 | Maximum videos returned over all inputs. |
| `maxRequests` | integer | `200` | 1 to 1,000 | Maximum page requests of the run (listing pages, load-more steps, video pages). |
| `maxPagesPerSource` | integer | `100` | 1 to 100 | Maximum listing pages for one search, profile or tag. |
| `maxScrollsPerPage` | integer | `100` | 1 to 100 | Maximum "load more" steps on one listing page. |
| `maxRequestRetries` | integer | `2` | 0 to 5 | Retries after a network error or a server error. |
| `requestTimeoutSecs` | integer | `30` | 5 to 120 | Time limit of one page request, in seconds. |

Use at least one of `startUrls`, `searchQueries`, `usernames`, `tags` or `videoUrls`. When a cap ends the run, `stopReason` says which (see Run stats).

### 📦 Output: XFree video data

Every full row has all of these fields. A missing text, number or object is `null`. A missing list is `[]`.

| Field | Type | Meaning |
|---|---|---|
| `recordType`, `schemaVersion`, `source` | text | Always `video`; the output schema version; the site (`xfree`). |
| `id` | text | The site's id of the video. |
| `url` | text | The video's page on xfree.com (the HTML page, not the file). |
| `sourceType` | text | `search`, `profile`, `tag` or `direct_video`. |
| `sourceUrl` | text | The input that first found the video. |
| `title` | text or null | Visible title, when the site shows one. |
| `creator` | object or null | `username`, `displayName`, `profileUrl`, `onlyfansUrl` of the account that posted it. |
| `taggedAccounts` | list | Other accounts shown on the video page, without the creator. Each has `name` and `profileUrl`. |
| `views` | integer or null | Views as a number. |
| `likes` | integer or null | Likes. |
| `durationSeconds` | number or null | Length in seconds. |
| `thumbnailUrl` | text or null | Preview image URL. |
| `tags` | list | Tags without `#`, in the site's order, no repeats. Hashtags only. |
| `videoFileUrl` | text or null | **Direct link to the video file** (`.mp4`, full quality). Play or download it. |
| `previewVideoUrl` | text or null | Direct link to the smaller `.mp4` copy the site plays in its lists. |
| `dates` | object | `posted` (when the video was posted) and `scraped` (when the row was made), ISO 8601 UTC. |

#### Example: a full row

An example row (`videoUrls` input). The tag list is shortened.

```json
{
  "recordType": "video",
  "schemaVersion": "4.0.0",
  "source": "xfree",
  "id": "123456789",
  "url": "https://www.xfree.com/video?id=123456789",
  "sourceType": "direct_video",
  "sourceUrl": "https://www.xfree.com/video?id=123456789&user=examplecreator",
  "title": "Example video title",
  "creator": { "username": "examplecreator", "displayName": "Example Creator", "profileUrl": "https://www.xfree.com/examplecreator", "onlyfansUrl": "https://onlyfans.com/examplecreator" },
  "taggedAccounts": [
    { "name": "exampletagged1", "profileUrl": "https://www.xfree.com/tag/exampletagged1" },
    { "name": "exampletagged2", "profileUrl": "https://www.xfree.com/tag/exampletagged2" }
  ],
  "views": 123456,
  "likes": 1234,
  "durationSeconds": 25.067,
  "thumbnailUrl": "https://thumbs.xfree.com/listing/medium_retina/12345678-aaaa-bbbb-cccc-123456789abc_3.jpg",
  "tags": ["Amateurs", "Teen", "Tits", "Brunette", "Blonde", "SmallTits", "Latina", "Solo"],
  "videoFileUrl": "https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/full.mp4",
  "previewVideoUrl": "https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/listing7.mp4?3",
  "dates": { "posted": "2025-01-01T12:00:00Z", "scraped": "2025-01-02T12:00:00Z" }
}
```

#### Example: a links-only row

```json
{
  "url": "https://www.xfree.com/video?id=123456789",
  "videoFileUrl": "https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/full.mp4",
  "previewVideoUrl": "https://cdn.example/xfree-prod/1/2/3/12345678-aaaa-bbbb-cccc-123456789abc/listing7.mp4?1"
}
```

### 📊 Run stats

Each run saves two records in the key-value store. `OUTPUT`:

| Key | Meaning |
|---|---|
| `status` | `success`, `partial` (some inputs failed, rows kept) or `failed`. |
| `sourcesProcessed` | Inputs read. |
| `listingPagesVisited`, `loadMoreSteps` | Listing pages read; "load more" steps made. |
| `videoCardsFound` | Video cards seen, before dedupe. |
| `uniqueVideosEmitted` | Rows written. |
| `duplicatesSkipped` | Videos already written. |
| `mediaUrlsFound`, `mediaUrlsMissing` | Rows with / without a `videoFileUrl`. |
| `requestsUsed` | Page requests made. |
| `stoppedEarly` | `true` when a cap ended the run. |
| `stopReason` | The cap: `maxResults`, `maxRequests`, `maxPagesPerSource`, `maxScrollsPerPage` or `chargeLimit`; else `null`. |
| `sourceErrors` | List of inputs that failed, with the message. |
| `sources` | One entry per input: type, URL, status, pages, videos. |
| `accountProfiles` | The profile data, once per profile read. |
| `chargedEvents` | How many times each billing event was charged. |

`RUN_HEALTH`: `{"status": "ok" | "degraded" | "failed", "reason": ...}`. `reason` says why a run is not `ok`.

### 💰 Pricing

Pay per event. There is no start fee.

| Event | Price | When it is charged |
|---|---|---|
| `video-scraped` | $0.0010 ($1.00 per 1,000) | Each unique video row written, with its direct video links. Links-only rows too. |

- 1,000 videos, each with its direct `.mp4` link: $1.00.
- Duplicate videos are free. The links are included in the video price.

### ❓ FAQ

#### How do I export XFree videos to Excel or CSV?

Run the actor, open the Output tab and pick Excel or CSV in the export options. JSON, XML and HTML are there too, and the Apify API returns the same data.

#### How do I get direct .mp4 links in my XFree video data?

Every row has `videoFileUrl` (full quality) and `previewVideoUrl`. Turn on `onlyVideoUrls` for rows with only the links, the fastest run.

### Data notice

This actor returns only what the site shows publicly at run time. We don't store or keep that data and are not its controller — you decide how you use it.

### 💬 Feedback and custom work

Need another portal, a field or a custom feed? Open an issue on the Issues tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/datawatcher/xfree-scraper/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Site URLs of any supported kind: search, account profile, tag or video page. The kind is found from the URL. xfree.com URLs only: https://www.xfree.com/search?q=..., https://www.xfree.com/<username>, https://www.xfree.com/tag/<tag>, https://www.xfree.com/video?id=...

## `searchQueries` (type: `array`):

Searches, each {"query": "...", "filter": "all" | "post" | "tag"} (filter optional, default "all"). A plain text line is read as {"query": text, "filter": "all"}. "post" and "tag" are the site's own search tabs (\&f=post, \&f=tag).

## `usernames` (type: `array`):

Account handles, with or without "@", or profile URLs. Every video of each account.

## `tags` (type: `array`):

Tag names exactly as the site writes them (case and prefixes kept, e.g. pl-Amatorzy), or tag page URLs.

## `tagSort` (type: `string`):

The order of tag pages. Empty - the site's default order.

## `videoUrls` (type: `array`):

Video page URLs. Each is read directly; one row per video.

## `onlyVideoUrls` (type: `boolean`):

Each row is only the three video links {"url", "videoFileUrl", "previewVideoUrl"}: the video page and its direct .mp4 files. Same price per video.

## `maxResults` (type: `integer`):

Maximum number of videos returned, in total. Shared fairly over the inputs (each search, profile, tag or video URL gets its part; what one cannot fill goes to the others).

## `maxRequests` (type: `integer`):

Maximum number of page requests the run makes, in total (listing pages and load-more steps; a direct video URL is one request).

## `maxPagesPerSource` (type: `integer`):

Maximum number of listing pages read for one search, profile or tag.

## `maxScrollsPerPage` (type: `integer`):

Maximum number of "Show more" / load-more steps on one listing page.

## `maxRequestRetries` (type: `integer`):

Retries of a request that failed on a network error or a server error. A block (403, 429, a challenge page) is never retried.

## `requestTimeoutSecs` (type: `integer`):

Time limit of one page request.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.xfree.com/tag/Sexy"
  ],
  "searchQueries": [
    {
      "query": "demicard",
      "filter": "all"
    }
  ],
  "onlyVideoUrls": false,
  "maxResults": 100,
  "maxRequests": 200,
  "maxPagesPerSource": 100,
  "maxScrollsPerPage": 100,
  "maxRequestRetries": 2,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `results` (type: `string`):

One row per video (or, with Only video page URLs, one {videoUrl} row per video).

## `runSummary` (type: `string`):

OUTPUT: status, sources, pages and load-more steps, video pages read, unique videos, duplicates, media URLs found, challenges, requests used, early stop and its reason, source errors, account profiles, charged events.

## `runHealth` (type: `string`):

{status, reason}: ok, degraded, or failed.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.xfree.com/tag/Sexy"
    ],
    "searchQueries": [
        {
            "query": "demicard",
            "filter": "all"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datawatcher/xfree-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": ["https://www.xfree.com/tag/Sexy"],
    "searchQueries": [{
            "query": "demicard",
            "filter": "all",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("datawatcher/xfree-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.xfree.com/tag/Sexy"
  ],
  "searchQueries": [
    {
      "query": "demicard",
      "filter": "all"
    }
  ]
}' |
apify call datawatcher/xfree-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datawatcher/xfree-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/cqcnHwlbae9gRHfkB/builds/zMtbkaWBTTqdHN5Lu/openapi.json
