# Pornhub Video Data Downloader (`maximedupre/pornhub-downloader`) Actor

Collect public Pornhub video data from direct video URLs, listing URLs, or search terms. Export metadata, engagement, taxonomy, channel details, thumbnails, and source-visible HLS links.

- **URL**: https://apify.com/maximedupre/pornhub-downloader.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Videos, Developer tools, Automation
- **Stats:** 70 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.45 / 1,000 scraped videos

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### 🎬 Turn Pornhub targets into video data

Pornhub Downloader helps researchers, content teams, and developers collect public [Pornhub](https://www.pornhub.com/) video data without copying pages by hand. Each accepted row can include the source view key, title, description, duration, upload date, thumbnail, engagement, taxonomy, channel details, discovery context, and source-visible media links. This gives you structured data for content review, cataloging, moderation research, media work, or an API workflow.

- Search public Pornhub listings by a topic with **[Pornhub Video Search](https://apify.com/maximedupre/pornhub-downloader/examples/pornhub-video-search)**.
- Read title, duration, engagement, and source links for one public video with **[Pornhub Video Metadata](https://apify.com/maximedupre/pornhub-downloader/examples/pornhub-video-metadata)**.
- Turn public video pages into structured metadata with **[Pornhub Metadata Scraper](https://apify.com/maximedupre/pornhub-downloader/examples/pornhub-metadata-scraper)**.
- Collect public video rows for a catalog with **[Pornhub Video Scraper](https://apify.com/maximedupre/pornhub-downloader/examples/pornhub-video-scraper)**.
- Get source-visible media links from a public video page with **[Pornhub Video Downloader](https://apify.com/maximedupre/pornhub-downloader/examples/pornhub-video-downloader)**.

#### 📦 Pornhub video rows at a glance

Each saved row represents one accepted public video. A direct video URL asks for that exact video and leaves `discovery` empty. A listing URL or search term adds the first saved match's query, listing URL, and rank when available. If the same source video appears again from another target, the Actor keeps the first eligible row and does not merge later matches into it.

**Public source data**

The Actor keeps values that Pornhub shows on the page. Missing descriptions, counts, taxonomy, channel facts, thumbnails, media links, or discovery details stay empty instead of being guessed. The Actor handles public pages only. It does not use a Pornhub account, cookies, Premium access, private content, or a browser extension.

#### ▶️ Run exact videos or discover matching pages

1. Add one direct video URL, listing URL, or search term per row in **Pornhub targets**.
2. Use a direct video URL when you know the exact video. Use a listing URL or search term for discovery.
3. Set **Maximum discovery videos** to limit videos found by listing URLs and search terms. Leave it empty to return all available results until the source is exhausted.
4. Choose the production and duration filters for discovery targets. Direct video URLs ignore these filters.
5. Start the Actor and open the default dataset.

#### ⚙️ Input

**Input fields**

| Field | Type | What it does |
| --- | --- | --- |
| `targets` | array of strings | Required. Adds 1 to 500 non-empty direct video URLs, listing URLs, or search terms, with one target per row. Direct video URLs request one exact video. Listing URLs and search terms start discovery. |
| `maxItems` | integer | Sets an optional positive limit for videos found by listing URLs and search terms. Leave it empty to return all available results until the source is exhausted. Direct video URLs do not use this limit. |
| `production` | string | For discovery, keeps all videos or chooses `professional` or `homemade`. The default is `all`. Direct video URLs ignore this choice. |
| `minDuration` | string | For discovery, keeps videos at or above the selected length. Choose `none`, `10`, `20`, or `30` minutes. The default is `none`. Direct video URLs ignore this filter. |
| `maxDuration` | string | For discovery, keeps videos at or below the selected length. Choose `none`, `10`, `20`, or `30` minutes. The default is `none`. Direct video URLs ignore this filter. |

**Example input**

The example below is the public input from a successful default-input run.

```json
{
  "targets": [
    "music video",
    "https://www.pornhub.com/video/search?search=music+video",
    "https://www.pornhub.com/view_video.php?viewkey=ph5f1b2c3d4e5f6"
  ],
  "maxItems": 1,
  "production": "all",
  "minDuration": "none",
  "maxDuration": "none"
}
```

#### 🧾 Output

The `results` link opens the default dataset. Each saved dataset row has the same public shape.

**Output fields**

| Field | Type | What it does |
| --- | --- | --- |
| `results` | string | Opens the default dataset overview with the saved video rows. |
| `viewKey` | string | Gives Pornhub's source view key for the video. |
| `title` | string | Gives the video title shown by Pornhub. |
| `description` | string or null | Gives the source description when available. |
| `durationSeconds` | integer or null | Gives the video length in seconds when available. |
| `uploadDate` | date string or null | Gives the source upload date in `YYYY-MM-DD` form when available. |
| `thumbnailUrl` | string or null | Links to the source thumbnail when available. |
| `engagement` | object | Groups the source engagement values. |
| `engagement.views` | integer or null | Gives the view count when available. |
| `engagement.upvotes` | integer or null | Gives the upvote count when available. |
| `engagement.likePercent` | number or null | Gives the source like percentage when available. |
| `taxonomy` | object | Groups categories, tags, performers, and production. |
| `taxonomy.categories` | array of strings | Lists categories attached to the video. |
| `taxonomy.tags` | array of strings | Lists tags attached to the video. |
| `taxonomy.performers` | array of strings | Lists performers shown for the video. |
| `taxonomy.production` | string or null | Gives the source production label when available. |
| `channel` | object | Groups the uploader or channel facts. |
| `channel.name` | string or null | Gives the channel or uploader name when available. |
| `channel.url` | string or null | Links to the channel or uploader when available. |
| `channel.videoCount` | integer or null | Gives the source channel video count when available. |
| `channel.subscriberCount` | integer or null | Gives the source channel subscriber count when available. |
| `media` | object | Groups source-visible HLS media links. |
| `media.masterPlaylistUrl` | string or null | Links to the master HLS playlist when available. |
| `media.variants` | array of objects | Lists the source media variants that are available. |
| `media.variants[].quality` | string or null | Gives a quality label for a media variant when available. |
| `media.variants[].width` | integer or null | Gives the variant width in pixels when available. |
| `media.variants[].height` | integer or null | Gives the variant height in pixels when available. |
| `media.variants[].contentType` | string or null | Gives the media content type when known. |
| `media.variants[].mediaUrl` | string | Links to the source media variant. |
| `discovery` | object | Groups search or listing context for a discovered video. It is empty for an exact direct target. |
| `discovery.query` | string or null | Gives the search term that produced the first saved match when applicable. |
| `discovery.listingUrl` | string or null | Links to the listing or search page that produced the first saved match when applicable. |
| `discovery.rank` | integer or null | Gives the rank of the first saved match in its discovery target when available. |

**Example video row**

This complete row is copied from a successful run. It is a discovery row, so its `discovery` object shows the first saved search match.

```json
{
  "viewKey": "66ca880f9e624",
  "title": "LEO WILD GIVES ME THE BEST EROTIC MASSAGE OF MY LIFE",
  "description": "Watch LEO WILD GIVES ME THE BEST EROTIC MASSAGE OF MY LIFE in Spanish on Pornhub.com, the best hardcore porn site. Pornhub is home to the widest selection of Porn in Spanish.",
  "durationSeconds": 2810,
  "uploadDate": "2024-08-25",
  "thumbnailUrl": "https://ei.phncdn.com/videos/202408/25/456911691/original/(m=q8V1NTZbeaAaGwObaaaa)(mh=BUuShCb8q0p-tkQw)0.jpg",
  "engagement": {
    "views": 494685,
    "upvotes": 1344,
    "likePercent": 100
  },
  "taxonomy": {
    "categories": [
      "Big Ass",
      "Big Dick",
      "Brunette",
      "HD Porn",
      "Hardcore",
      "Latina",
      "MILF",
      "Massage",
      "Small Tits",
      "Verified Models"
    ],
    "tags": [
      "massage rooms",
      "thai massage",
      "hot milf",
      "latina milf",
      "mature milf",
      "big cockk",
      "big cock",
      "cum in mouth",
      "pussy eating",
      "prone bone",
      "doggystyle",
      "spread eagle",
      "pussy rubbing",
      "moaning"
    ],
    "performers": [],
    "production": "homemade"
  },
  "channel": {
    "name": "Kathy Lee69",
    "url": "https://www.pornhub.com/pornstar/kathy-lee69",
    "videoCount": 157,
    "subscriberCount": 221000
  },
  "media": {
    "masterPlaylistUrl": "https://ev-h.phncdn.com/hls/videos/202408/25/456911691/1080P_4000K_456911691.mp4/master.m3u8?validfrom=1789028962&validto=1789036162&ipa=1&hdl=-1&hash=D1%2BmBcJhdmrZY6g5D6qQiSKh4n4%3D",
    "variants": [
      {
        "quality": "1080p",
        "width": 1920,
        "height": 1080,
        "contentType": "application/vnd.apple.mpegurl",
        "mediaUrl": "https://ev-h.phncdn.com/hls/videos/202408/25/456911691/1080P_4000K_456911691.mp4/master.m3u8?validfrom=1789028962&validto=1789036162&ipa=1&hdl=-1&hash=D1%2BmBcJhdmrZY6g5D6qQiSKh4n4%3D"
      },
      {
        "quality": "1080p",
        "width": 1920,
        "height": 1080,
        "contentType": "mp4",
        "mediaUrl": "https://www.pornhub.com/video/get_media?s=eyJrIjoiNjdiZTE1Y2FjOWQyMDQxZGNjYTRlNDAyYTM4YjM2Nzk2NTg0YWZhNWI3ZDJkOWIxNDk4ZTJhODk2YjdiNmIyYyIsInQiOjE3ODkwMzI1NjJ9&v=66ca880f9e624&e=0&t=p"
      },
      {
        "quality": "720p",
        "width": 1280,
        "height": 720,
        "contentType": "application/vnd.apple.mpegurl",
        "mediaUrl": "https://ev-h.phncdn.com/hls/videos/202408/25/456911691/720P_4000K_456911691.mp4/master.m3u8?validfrom=1789028962&validto=1789036162&ipa=1&hdl=-1&hash=yGDumeTOIV6kg58WwLvzY%2F0AZXk%3D"
      },
      {
        "quality": "480p",
        "width": 854,
        "height": 480,
        "contentType": "application/vnd.apple.mpegurl",
        "mediaUrl": "https://ev-h.phncdn.com/hls/videos/202408/25/456911691/480P_2000K_456911691.mp4/master.m3u8?validfrom=1789028962&validto=1789036162&ipa=1&hdl=-1&hash=ooxmFvoqvYugC3hXX4AKBe8Wulc%3D"
      },
      {
        "quality": "240p",
        "width": 426,
        "height": 240,
        "contentType": "application/vnd.apple.mpegurl",
        "mediaUrl": "https://ev-h.phncdn.com/hls/videos/202408/25/456911691/240P_1000K_456911691.mp4/master.m3u8?validfrom=1789028962&validto=1789036162&ipa=1&hdl=-1&hash=F3XF6l1U09WBv3bPkR1Gk10MXkk%3D"
      }
    ]
  },
  "discovery": {
    "query": "music video",
    "listingUrl": "https://www.pornhub.com/video/search?search=music+video",
    "rank": 1
  }
}
```

Source-visible media links can expire or change. Use them while available, or rerun the Actor when you need fresh links.

#### 💳 Pricing

**Charged event**

This Actor uses pay-per-event pricing. The `Scraped video` event costs `$0.00445` for one accepted public video saved with its available metadata and source-visible media links.

**Control discovery work**

Use **Maximum discovery videos** for a smaller test or to limit discovery work. Leave it empty to return all available results until the source is exhausted. A target that produces no accepted video does not create this video event.

#### 🔌 Integrations

**Ways to use the data**

Open the dataset in Apify, export it as JSON, CSV, Excel, XML, RSS, or HTML, or read it through the Apify API. You can schedule repeat runs and use webhooks in an Apify workflow.

https://www.youtube.com/watch?v=bNACk1\_S\_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### What happens when I give a direct regional video URL?

Direct video URLs, including regional Pornhub hostnames, are treated as exact targets. The Actor returns that video only when it is available. It does not turn an unavailable direct URL into search results, and a direct row has empty discovery context.

##### Can I mix direct URLs, listing URLs, and search terms?

Yes. Add each target as a separate row. Direct URLs request one exact video. Listing URLs and search terms discover matching public videos.

##### How does the discovery limit work?

`maxItems` limits videos found by listing URLs and search terms. Leave it empty to return all available results until the source is exhausted. It does not limit direct video URLs.

##### What happens when the same video matches two targets?

The Actor saves the first eligible occurrence and ignores later matches of that source video. It does not merge later submitted targets or discovery matches into the saved row. Its discovery context describes only the first saved match.

##### Which fields can be empty?

Pornhub may not show every value on every page. Descriptions, engagement values, taxonomy values, channel facts, thumbnails, media links, and discovery details can be empty when unavailable.

##### Does this Actor save video files?

No. It returns public video metadata and source-visible HLS media links. It does not copy video files into Apify storage or create ZIP archives.

##### Does it need a login, cookies, or a source API key?

No. It reads public pages and does not use a Pornhub account, cookies, Premium access, a browser extension, or a source API key.

##### How can I use the saved data in another app?

Read the dataset through the Apify API or export it in a format supported by your workflow.

### 📝 Changelog

**v1.0**

- Treats regional Pornhub video URLs as exact targets and leaves discovery context empty.
- Keeps only genuine per-video categories, tags, and performers while leaving unavailable values empty.

**v0.1**

- Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~pornhub-downloader/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [XNXX Video Downloader](https://apify.com/maximedupre/xnxx-video-downloader) - Resolve known public XNXX video URLs or IDs into source media links and metadata.
- [RedGifs Bulk Downloader](https://apify.com/maximedupre/redgifs-bulk-downloader) - Collect public RedGifs video links and metadata from URLs, IDs, or search.
- [ManyVids Video Downloader](https://apify.com/maximedupre/manyvids-video-downloader) - Resolve known public ManyVids video pages into media links and creator details.
- [EroThots Video Downloader](https://apify.com/maximedupre/erothots-media-downloader) - Get direct MP4 links and metadata from public EroThots video URLs or IDs.
- [OnlyFans Downloader](https://apify.com/maximedupre/onlyfans-downloader) - Export source-hosted media links and post details from public OnlyFans targets.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `targets` (type: `array`):

Add 1 to 500 non-empty direct video URLs, listing URLs, or search terms, with one target per row. A direct video URL requests one exact video. A listing URL or search term starts discovery.

## `maxItems` (type: `integer`):

Set an optional limit for videos found from listing URLs and search terms. Leave it empty to return all available results until the source is exhausted. Direct video URLs do not use this limit. If set, use 1 or more.

## `production` (type: `string`):

For listing URLs and search terms, choose all videos, professional videos, or homemade videos. Direct video URLs ignore this choice.

## `minDuration` (type: `string`):

For listing URLs and search terms, keep videos at or above the selected length. Choose no minimum, 10, 20, or 30 minutes. Direct video URLs ignore this filter.

## `maxDuration` (type: `string`):

For listing URLs and search terms, keep videos at or below the selected length. Choose no maximum, 10, 20, or 30 minutes. Direct video URLs ignore this filter.

## Actor input object example

```json
{
  "targets": [
    "music video",
    "https://www.pornhub.com/video/search?search=music+video",
    "https://www.pornhub.com/view_video.php?viewkey=ph5f1b2c3d4e5f6"
  ],
  "maxItems": 1,
  "production": "all",
  "minDuration": "none",
  "maxDuration": "none"
}
```

# Actor output Schema

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

Open the dataset with one accepted public Pornhub video per row.

# 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 = {
    "targets": [
        "music video",
        "https://www.pornhub.com/video/search?search=music+video",
        "https://www.pornhub.com/view_video.php?viewkey=ph5f1b2c3d4e5f6"
    ],
    "maxItems": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/pornhub-downloader").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 = {
    "targets": [
        "music video",
        "https://www.pornhub.com/video/search?search=music+video",
        "https://www.pornhub.com/view_video.php?viewkey=ph5f1b2c3d4e5f6",
    ],
    "maxItems": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/pornhub-downloader").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 '{
  "targets": [
    "music video",
    "https://www.pornhub.com/video/search?search=music+video",
    "https://www.pornhub.com/view_video.php?viewkey=ph5f1b2c3d4e5f6"
  ],
  "maxItems": 1
}' |
apify call maximedupre/pornhub-downloader --silent --output-dataset

```

## MCP server setup

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

```

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/0oVHkrJkQQl15kglg/builds/EftHfJXPwQNKQi6mL/openapi.json
