Other Options:
• PayPal
• Ko-Fi
For extended features, enterprise integrations, or custom commercial licensing, please contact EchterAlsFakeBS@proton.me.
Installation
Install from PyPI using pip:
pip install unofficial-api-for-pornhub
For TS→MP4 remuxing support (recommended for HLS downloads), install with the optional av dependency:
pip install unofficial-api-for-pornhub[av]
eaf_base_api ≥ 4.0.0 is installed automatically as a dependency.
Quick Start
Every method in this API is asynchronous. You need to run your code inside an async function:
import asyncio
from pornhub_api import Client
async def main():
client = Client()
# Fetch a video
video = await client.get_video("https://www.pornhub.com/view_video.php?viewkey=...")
# Access metadata
print(video.title)
print(video.views)
print(video.tags)
# Download the video
from base_api import DownloadConfigHLS
config = DownloadConfigHLS(quality="best", path="./downloads")
await video.download(configuration=config)
asyncio.run(main())
Configuration
The API uses eaf_base_api ≥ 4.0.0 for networking. Configure its singular proxy, bounded request attempts, timeouts, and other runtime behavior through a custom BaseCore.
Please refer to the eaf_base_api Documentation for the complete reference on how to set up RuntimeConfig and properly integrate it with this API.
from base_api import BaseCore
from base_api.modules.config import RuntimeConfig
from pornhub_api import Client
my_config = RuntimeConfig()
my_config.proxy = "socks5://127.0.0.1:9050"
my_config.request_attempts = 3
core = BaseCore(configuration=my_config)
client = Client(core=core)
Client
The Client class is your entry point for all interactions. It manages the session, headers, cookies, and authentication — providing methods to fetch individual resources, search, and access account features.
from pornhub_api import Client
from base_api import BaseCore
# Basic initialization
client = Client()
# Construct without logging in
client = Client(
core=BaseCore(),
email="user@example.com",
password="password",
)
await client.login()
# Or await the factory for construction plus completed login
client = await Client.create(
core=BaseCore(),
email="user@example.com",
password="password",
login=True,
)
Constructor Parameters
- core BaseCore | None — Optional networking core instance. If omitted or
None(default), a freshBaseCoreinstance is initialized with default headers and cookies. - email str | None — Account email for login
- password str | None — Account password for login
Methods
Fetch Video get_video()
Video object. By default, loads metadata via the Webmaster API; set load_html=True for full HTML-scraped data including categories, tags, and author information.Parameters
- url str — The full Pornhub video URL
- load_html bool — If
True, fetches and parses the full HTML page for extended metadata (categories, tags, author info, m3u8 URLs) - load_api bool — If
True(default), fetches metadata via the Webmaster API for faster loading
Returns
→ VideoFetch Pornstar get_pornstar()
Pornstar object with bio, about, and info fields.Parameters
- url str — The full Pornhub pornstar profile URL
- load_html bool — If
True(default), parses the HTML profile page for metadata
Returns
→ PornstarFetch Model get_model()
Model object.Parameters
- url str — The full Pornhub model profile URL
- load_html bool — If
True(default), parses the HTML profile page
Returns
→ ModelFetch User get_user()
User object.Parameters
- url str — The full Pornhub user profile URL
- load_html bool — If
True(default), parses the HTML profile page
Returns
→ UserFetch Channel get_channel()
Channel object with name, stats, and metadata.Parameters
- url str — The full Pornhub channel URL
- load_html bool — If
True(default), parses the HTML page
Returns
→ ChannelFetch Playlist get_playlist()
Playlist object with title, description, tags, and video counts.Parameters
- url str — The full Pornhub playlist URL
- load_html bool — If
True(default), parses the HTML page for metadata
Returns
→ PlaylistFetch Album get_album()
Album object with rating, views, tags, and author information.Parameters
- url str — The full Pornhub album URL
- load_html bool — If
True(default), parses the HTML page
Returns
→ AlbumFetch GIF get_gif()
GIF object with title, votes, views, tags, and direct download URL.Parameters
- url str — The full Pornhub GIF URL
- load_html bool — If
True(default), parses the HTML page
Returns
→ GIFFetch Short get_short()
Short object with title, likes, and streaming URLs.Parameters
- url str — The full Pornhub short URL
- load_html bool — If
True(default), parses the HTML page
Returns
→ ShortSearch Videos search_videos()
Parameters
- query str — Search keywords
- production_type str | None — Filter by
"professional"or"homemade" - sort_by str | None —
"mr"(Most Recent),"mv"(Most Viewed),"tr"(Top Rated) - duration_min str | None — Minimum duration in minutes:
"10","20", or"30" - duration_max str | None — Maximum duration in minutes:
"10","20", or"30" - pages int — Number of result pages to iterate (default:
5) - iterator_config IteratorConfig | None — Optional v4 concurrency, ordering, eager-source, retry, and error-handling policy. By default, no media source is loaded eagerly.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Search GIFs search_gifs()
Parameters
- query str — Search keywords
- category str | None — Category filter:
"gay"or"transgender"(default: straight) - search_filter str | None —
"mr"(Most Recent),"mv"(Most Viewed),"tr"(Top Rated) - pages int — Number of result pages (default:
5) - iterator_config IteratorConfig | None — Optional v4 iterator policy. By default, no media source is loaded eagerly.
Returns
→ AsyncGenerator[ScrapeResult[GIF], None]Search HubTraffic API search_hubtraffic()
Parameters
- query str — Search keywords
- category str | None — Category filter string
- sort_by str | None —
"newest","mostviewed", or"rating" - period str | None — Time period:
"weekly","monthly", or"alltime" - pages int — Number of result pages (default:
5) - iterator_config IteratorConfig | None — Optional v4 iterator policy. The default uses higher page/item concurrency for HubTraffic but still performs no eager media-source load.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Create Client create()
login=True and credentials are provided, awaits login() before returning the instance. This is the recommended entry point when creating an authenticated client.Parameters
- core BaseCore | None — Optional custom networking core instance
- email str | None — Account email
- password str | None — Account password
- login bool — If
Trueand credentials are provided, automatically awaits login
Returns
→ ClientLogin login()
client.account with user data and enables access to account-specific methods (recommended, history, favorites, feed, subscriptions).Parameters
- force bool — If
True, re-login even if already logged in - throw bool — If
True(default), raisesLoginFailedon failure; ifFalse, returnsFalseinstead
Returns
→ boolRaises
- LoginFailed — If credentials are invalid or login request fails
- ClientAlreadyLogged — If already logged in and
force=False
Video
dataclass Inherits from BaseMedia. Represents a single video with metadata from both HTML scraping and the Webmaster API. Supports dual loading modes: API-only (fast) and HTML (full data including m3u8 URLs for downloading).
Attributes
| Attribute | Type | Source | Description |
|---|---|---|---|
url | str | — | The video page URL |
video_id | str | None | — | Extracted viewkey from URL |
title | str | None | API / HTML | Video title |
duration | int | None | API / HTML | Video duration in seconds |
thumbnail | str | None | API / HTML | Preview thumbnail URL |
views | str | None | API / HTML | View count |
likes | int | str | None | API / HTML | Like / rating count |
publish_date | str | None | API / HTML | Upload / publish date |
categories | list[str] | None | API / HTML | Category names |
tags | list[str] | None | API / HTML | Tag names |
rating_percent | str | float | None | API | Webmaster API rating percentage |
is_hd | bool | None | HTML | Whether the video is HD |
is_vr | bool | None | HTML | Whether the video is VR |
is_vertical | bool | None | HTML | Whether the video is vertical (portrait) |
is_video_unavailable | bool | None | HTML | Whether the video is unavailable |
is_video_unavailable_in_your_country | bool | None | HTML | Whether the video is geo-blocked |
available_qualities | list[int] | None | HTML | Sorted list of available resolution heights |
m3u8_base_url | str | None | HTML | Synthesized master m3u8 playlist for HLS download |
author_thumbnail | str | None | HTML | Author's avatar image URL |
author_link | str | None | HTML | Author's profile URL |
author_information | dict | None | HTML | Author details: name, link, video count, subscriber count |
client.get_video(url, load_html=True) or by calling await video.load_sources("html") afterward). The m3u8 streaming URLs are only available from that source.
Methods & Properties
Download Video download()
no_title=True is set on the config.Parameters
- configuration DownloadConfigHLS — HLS download settings. See Downloading.
Returns
→ bool | DownloadReportGet Author author
Pornstar, Model, or Channel based on the author-link pattern. The source-aware property loads the author link on demand.Returns
→ Pornstar | Model | Channel | NonePornstar
dataclass Inherits from UserHelper → BaseMedia. Represents a pornstar profile. Shared base with Model and User for profile info and video iteration.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The pornstar profile URL |
name | str | None | Profile display name |
bio | str | None | Short bio / tagline text |
about | str | None | Extended about section text |
info | dict | None | Structured info fields (e.g., gender, age, location) |
Methods
Get Videos get_videos()
Parameters
- pages int — Number of profile pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default does not eagerly load
htmlorapi.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Uploads get_uploads()
Parameters
- pages int — Number of upload pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get GIFs get_gifs()
Parameters
- pages int — Number of GIF pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[GIF], None]Model
dataclass Inherits from UserHelper → BaseMedia. Represents a model profile and inherits get_videos() from UserHelper.
Attributes
Same as Pornstar: url, name, bio, about, info.
Methods
Get Videos get_videos()
UserHelper.Parameters
- pages int — Number of profile pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]User
dataclass Inherits from UserHelper → BaseMedia. Represents a regular user profile and inherits get_videos() from UserHelper.
Attributes
Same as Pornstar: url, name, bio, about, info.
Methods
Get Videos get_videos()
UserHelper.Parameters
- pages int — Number of profile pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Channel
dataclass Inherits from BaseMedia. Represents a production channel with studio-level metadata.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The channel page URL |
name | str | None | Channel display name |
is_award_winner | bool | None | Whether the channel has won awards |
video_views | str | None | Total video views across the channel |
subscribers | str | None | Subscriber count |
total_videos | str | None | Total number of videos |
rank | str | None | Channel rank |
description | str | None | Channel description text |
join_date | str | None | When the channel joined |
website | str | None | External website URL |
user_link | str | None | Associated user profile URL |
Methods
Get Videos get_videos()
Parameters
- pages int — Number of channel pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get User get_user()
User object associated with this channel.Returns
→ UserPlaylist
dataclass Inherits from BaseMedia. Represents a video playlist with title, description, tags, and video count. Uses the chunked API endpoint internally for video iteration.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The playlist page URL |
title | str | None | Playlist title |
description | str | None | Playlist description text |
views | str | None | View count |
rating_percent | str | None | Rating percentage |
likes | str | None | Like count |
dislikes | str | None | Dislike count |
video_count | str | None | Total number of videos in the playlist |
unavailable_videos | int | None | Number of hidden / unavailable videos |
tags | dict[str, str] | None | Tag name → URL mapping |
author_link | str | None | Playlist author's profile URL |
token | str | None | Internal token for chunked API requests |
playlist_id | str | None | Extracted playlist numeric ID |
Methods
Get Videos get_videos()
Parameters
- pages int — Number of chunked playlist pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default constructs results without eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Author get_author()
User who created the playlist.Returns
→ UserAlbum
dataclass Inherits from BaseMedia. Represents a photo album with rating, views, tags, and individual photo download support.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The album page URL |
rating_percentage | str | None | Album rating percentage |
views | str | None | View count |
publish_date | str | None | Publish date string |
tags | dict[str, str] | None | Tag name → URL mapping |
votes | str | None | Vote count string |
author_link | str | None | Album author's profile URL |
Methods
Get Photos get_photos()
url, download_url, rating, and views.Parameters
- pages int — Number of album pages to fetch (default:
1)
Returns
→ AsyncGenerator[dict, None]Each dict contains: url, download_url, rating, views
Download Photo download_photo()
download_url from get_photos() results.Parameters
- url str — Direct download URL of the photo
- path str — Output file path
Returns
→ boolGet Author author
Pornstar object who authored this album.Returns
→ PornstarGIF
dataclass Inherits from BaseMedia. Represents a single GIF with voting data, source video link, and a direct download URL.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The GIF page URL |
title | str | None | GIF title |
vote_count | str | None | Total vote count |
vote_percentage | str | None | Positive vote percentage |
views | str | None | View count |
publish_date | str | None | Upload date |
thumbnail | str | None | Thumbnail image URL |
content_url | str | None | Direct MP4/WebM download URL |
source_video_url | str | None | URL of the source video this GIF was created from |
tags | dict[str, str] | None | Tag name → URL mapping |
Methods
Download GIF download()
no_title=True.Parameters
- configuration DownloadConfigRAW — RAW download settings. See Downloading.
Returns
→ boolShort
dataclass Inherits from BaseMedia. Represents a Pornhub short-form video with metadata extracted from inline JSON.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The short page URL |
title | str | None | Short title |
video_id | str | None | Internal video ID |
video_key | str | None | Video key identifier |
likes | str | None | Like count |
dislikes | str | None | Dislike count |
favorites | str | None | Favorite count |
comment_count | str | None | Comment count |
is_hd | bool | None | Whether the short is HD |
thumbnail | str | None | Thumbnail image URL |
embed_url | str | None | Embed URL |
author_name | str | None | Author display name |
author_link | str | None | Author profile URL |
avatar | str | None | Author avatar image URL |
video_url | str | None | Link to the full video version |
m3u8_base_url | str | None | Synthesized master m3u8 playlist |
media_definitions | dict | None | Raw media quality definitions |
duration | int | None | Video duration in seconds |
categories | list[str] | None | Category labels |
tags | list[str] | None | Tag labels |
is_verified | bool | None | Whether the creator is verified |
like_count | int | None | Numeric parsed like count |
dislike_count | int | None | Numeric parsed dislike count |
like_info | str | None | Formatted like string |
favorite_info | str | None | Formatted favorite string |
token | str | None | Short action token |
author_id | str | None | Uploader numeric account ID |
author_type | str | None | Author membership type (e.g. "Mpp") |
external_link | str | None | Creator external link URL |
external_link_text | str | None | Call-to-action text for the external link |
large_preview_url | str | None | High-resolution preview image URL |
shortie_url | str | None | Canonical short URL identifier |
Methods
Download Short download()
no_title=True.Parameters
- configuration DownloadConfigHLS — HLS download settings. See Downloading.
Returns
→ bool | DownloadReportGet Author get_author()
Pornstar object who created this short.Returns
→ PornstarGet Video get_video()
Video object corresponding to this short.Parameters
- load_html bool — If
True, fetches full HTML page for video details - load_api bool — If
True(default), fetches metadata via the Webmaster API
Returns
→ VideoAccount
Represents the authenticated user account. Access via client.account after calling client.login(). Provides methods for recommendations, history, favorites, feed, and subscriptions.
Attributes
| Attribute | Type | Description |
|---|---|---|
name | str | None | Account username |
avatar | str | None | Avatar image URL |
is_premium | bool | Whether the account has premium status |
user | User | None | Associated User object for the account profile |
from pornhub_api import Client
client = Client(email="user@example.com", password="password")
await client.login()
print(client.account.name) # Username
print(client.account.is_premium) # True/False
# Get recommended videos
async for result in client.account.get_recommended():
if result.succeeded:
video = result.unwrap()
await video.load_sources("html")
print(video.title)
Methods
Get Recommended get_recommended()
Parameters
- pages int — Number of recommendation pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default performs no eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get History get_history()
Parameters
- pages int — Number of history pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default performs no eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Favorites get_favorites()
Parameters
- pages int — Number of favorites pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default performs no eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Feed get_feed()
Parameters
- section str — Section to filter:
"videos","photos","posts", etc. - pages int — Number of feed pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default performs no eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Subscriptions get_subscriptions()
Parameters
- pages int — Number of subscription pages.
- iterator_config IteratorConfig | None — Optional v4 iterator policy; the default performs no eager source loading.
Returns
→ AsyncGenerator[ScrapeResult[User], None]Search & Filtering
The PornHub API offers three distinct search methods with rich filtering options:
Video Search Filters
| Parameter | Values | Description |
|---|---|---|
production_type | "professional", "homemade" | Filter by content type |
sort_by | "mr", "mv", "tr" | Most Recent, Most Viewed, Top Rated |
duration_min | "10", "20", "30" | Minimum duration in minutes |
duration_max | "10", "20", "30" | Maximum duration in minutes |
GIF Search Filters
| Parameter | Values | Description |
|---|---|---|
category | "gay", "transgender" | Category filter (default: straight) |
search_filter | "mr", "mv", "tr" | Most Recent, Most Viewed, Top Rated |
HubTraffic API Filters
| Parameter | Values | Description |
|---|---|---|
category | Any category string | Free-form category filter |
sort_by | "newest", "mostviewed", "rating" | Sort order |
period | "weekly", "monthly", "alltime" | Time period filter |
# Search professional videos, sorted by most viewed
async for result in client.search_videos(
query="blonde",
production_type="professional",
sort_by="mv",
pages=3
):
if result.succeeded:
video = result.unwrap()
await video.load_sources("html")
print(video.title)
# Fast search via HubTraffic API
async for result in client.search_hubtraffic(
query="amateur",
sort_by="newest",
period="weekly"
):
if result.succeeded:
video = result.unwrap()
print(video.title, video.views)
Downloading
The PornHub API uses two download modes depending on the content type:
- HLS (
DownloadConfigHLS) — For Videos and Shorts. Downloads HLS streams with quality selection, threaded segment downloading, resume support, and optional TS→MP4 remuxing. - RAW (
DownloadConfigRAW) — For GIFs and Album photos. Direct file downloads with multi-threaded range request support.
HLS Download (Videos & Shorts)
from base_api import DownloadConfigHLS
config = DownloadConfigHLS(
quality="best", # "best", "half", "worst", or height int (e.g. 720)
path="./downloads", # Output directory or file path
no_title=False, # Auto-append video.title + ".mp4" if False
)
# Must have HTML loaded for m3u8 URLs
video = await client.get_video(url, load_html=True)
success = await video.download(configuration=config)
RAW Download (GIFs & Photos)
from base_api import DownloadConfigRAW
config = DownloadConfigRAW(
quality="best", # "best", "half", "worst", or height int
path="./downloads", # Output directory or file path
no_title=False, # Auto-append title + ".mp4" if False
allow_multipart=True, # Use multi-threaded range requests
max_workers=5 # Number of parallel download threads
)
gif = await client.get_gif(url)
success = await gif.download(configuration=config)
For full details on all download configuration options, refer to the eaf_base_api Documentation.
Pagination & Iterators
Methods like search_videos(), pornstar.get_videos(), channel.get_videos(), and the account iterators yield typed ScrapeResult[T] values. Use succeeded to branch safely or unwrap() to return the item and raise its terminal error on failure:
async for result in client.search_videos("college", pages=2):
if result.succeeded:
video = result.unwrap()
await video.load_sources("html")
print(video.title)
else:
print(result.stage, result.url, result.error)
ScrapeResult
| Attribute | Type | Description |
|---|---|---|
stage | ScrapeStage | Whether the result came from the page or item stage |
url | str | The video URL |
page_index | int | Zero-based source page index |
item_index | int | None | Zero-based item index, or None for a page failure |
attempts | int | Number of attempts used by the yielding stage |
item | T | None | The constructed Video, GIF, or User on success |
error | ScrapeOperationError | None | The typed terminal page or item error on failure |
succeeded | bool | True when error is None |
IteratorConfig, bounded retries, and custom handlers
All iterator-only controls now live in one IteratorConfig. Pornhub's package default uses ErrorMode.SKIP for terminal page failures; a supplied config replaces that behavior, and a bare IteratorConfig instead defaults to ErrorMode.YIELD. A retry policy's max_attempts includes the initial attempt, so this example makes at most three stage attempts per failed page or item. Each stage attempt may itself perform the request retries configured on BaseCore.
Leave page_retry or item_retry as None to derive that stage's bounded policy from the active RuntimeConfig request-attempt and backoff settings; an explicit RetryPolicy overrides it per stage.
Pornhub's default iterator configuration loads no media source eagerly: it constructs objects from listing data. Call await item.load_sources("html") or opt in with load_specific_sources=("html",), as below. Use "api" only when the selected media type supports that loader.
from base_api import ErrorAction, ResultOrder, RetryPolicy, ScrapeErrorContext
from base_api.modules.config import IteratorConfig
async def handle_scrape_error(context: ScrapeErrorContext) -> ErrorAction:
if context.attempt < context.max_attempts:
return ErrorAction.RETRY
return ErrorAction.YIELD
retry = RetryPolicy(
max_attempts=3, base_delay=0.5, multiplier=2.0,
max_delay=4.0, jitter=0.2
)
iterator_config = IteratorConfig(
max_page_concurrency=2,
max_item_concurrency=8,
max_pending_items=16,
order=ResultOrder.ORIGINAL,
load_specific_sources=("html",),
page_retry=retry,
item_retry=retry,
page_error_handler=handle_scrape_error,
item_error_handler=handle_scrape_error,
)
async for result in client.search_videos(
"college", pages=2, iterator_config=iterator_config
):
if result.succeeded:
print(result.unwrap().title)
else:
print(result.stage, result.error)
ScrapeErrorContext supplies stage, url, error, attempt, max_attempts, page_index, and item_index. A handler returns ErrorAction.RETRY, RAISE, YIELD, or SKIP.
The core routes page-stage failures to page_error_handler and item-stage failures to item_error_handler. Assign the same callable to both fields only when both stages should use the same policy; otherwise configure either handler independently.
Error Handling
All custom exceptions inherit from PornhubAPIError, which inherits from the shared ScraperException and BaseScraperError. Source loaders translate request failures into these library exceptions. Calls that load media expose ordinary loader failures through base_api.MediaLoadError (or MediaLoadErrors for several sources); inspect original_error/errors as shown. Operations outside media loading, such as login, raise package or core exceptions directly.
Request and download failures are logged with the operation, target URL, and full original traceback. Translated exceptions retain the original error in __cause__. Download preparation failures (including metadata loading, quality selection, and output path setup) are also wrapped in DownloadFailed; inspect its cause when diagnosing a failure. The specific availability exceptions listed below remain supported. An explicit DownloadCancelled or asyncio.CancelledError propagates without being wrapped in DownloadFailed. Base downloader False and DownloadReport results remain supported; inspect the result as well as handling exceptions.
The common provider errors NotFound, NetworkError, BotDetection, ProxyError, UnknownNetworkError, and DownloadFailed are catchable through base_api.modules.errors. They derive from ScraperException, which now derives from BaseScraperError. Existing provider import paths remain valid.
See Logging & Cleanup for application logging setup.
Pornhub retains its own exception classes: each common error also subclasses the corresponding shared error. For example, pornhub_api.modules.errors.DownloadFailed can be caught as either PornhubAPIError or base_api.modules.errors.DownloadFailed.
| Exception | When Raised |
|---|---|
PornhubAPIError | Base exception class for PornHub API errors; inherits from ScraperException |
NotFound | Server returned HTTP 404 |
NetworkError | Request failed due to a network error, exhausted request retries, or a non-404 HTTPStatusError |
BotDetection | Bot protection triggered |
ProxyError | Invalid or failing proxy |
UnknownNetworkError | Unexpected network errors |
DownloadFailed | Download preparation or transfer failed; the video URL is included and __cause__ retains the original exception |
LoginFailed | Authentication failed (invalid credentials or token) |
ClientAlreadyLogged | Attempted login when already authenticated |
VideoDisabled | The video has been disabled by the platform |
GifPendingReview | The GIF is still pending review and cannot be accessed |
from base_api import MediaLoadError
from pornhub_api.modules.errors import PornhubAPIError, NotFound, BotDetection, LoginFailed
try:
video = await client.get_video(url, load_html=True, load_api=False)
except MediaLoadError as error:
if isinstance(error.original_error, NotFound):
print("Video does not exist")
elif isinstance(error.original_error, BotDetection):
print("Bypassing bot protection failed")
elif isinstance(error.original_error, PornhubAPIError):
print("PornHub API error occurred")
else:
raise
try:
await client.login()
except LoginFailed as e:
print(f"Login failed: {e}")
CLI Usage
The CLI entry point configures console logging at INFO level. Caught per-URL failures include the URL and full traceback; the default log format shows the logger, file, line, and function. Library applications should configure logging once at startup.
The PornHub API package includes a built-in CLI accessible via phub, pornhub_api, or python -m pornhub_api:
# Download a single video
phub --download "https://www.pornhub.com/view_video.php?viewkey=..." --quality best --output ./downloads --no-title False
# Download from a file of URLs
phub --file urls.txt --quality best --output ./downloads --no-title False
# Download liked videos (requires login)
phub --liked --email user@example.com --password pass --quality best --output ./favorites --no-title False
# Download with video ID as filename and a limit
phub --download "https://www.pornhub.com/view_video.php?viewkey=..." --quality best --output ./downloads --no-title False --id-as-title --limit 10
CLI Options
| Flag | Description |
|---|---|
--download URL | Download from the specified URL (video, short, GIF, album, pornstar, model, user, channel, or playlist) |
--file FILE | Read and download URLs from a line-separated text file |
--quality QUALITY | Required. The video quality: best, half, worst |
--output DIR | Required. The destination path/directory |
--no-title True/False | Required. Skip auto-appending video title to output filename |
--pages N | Number of pages to fetch for iterables (default: 1) |
--email EMAIL | Account email for login |
--password PASS | Account password for login |
--id-as-title | Use the video ID as the output filename instead of the title |
--limit N | Maximum number of videos to download |
--liked | Download liked/favorite videos (requires login) |
--recommended | Download recommended videos (requires login) |
--watched | Download watched/history videos (requires login) |
Changelog
5.4.3 — 2026-09-15
d3c8743/81e4629/d034cf2— Adopted centralized request and download error handling frombase_api.modules.provider(requiringeaf-base-api>=4.2.0). Failed downloads now raiseDownloadFailedwith full context. Added dedicatedPornstarclass inheriting fromUserHelperandclient.get_pornstar(). Greatly expandedShortmetadata with 15 new attributes (duration, categories, tags, is_verified, like/dislike counts and info, token, author ID/type, external links, preview URLs). Added comprehensive unit test suites coveringShort,User,Album,Channel,Gif,Playlist, andVideo. Removed redundant code and streamlined scraping logic.
5.4.3 — 2026-09-06
f8bbe99— IntroducedPornhubAPIErrorbase exception class (inheriting fromException) for all library errors; convertedGifPendingReviewandVideoDisabledto inherit fromPornhubAPIErrorinstead ofBaseException. Removed redundantSubscriptionHelperclass. Refactored scrape methods (get_videos,get_uploads,get_gifs, search, and account iterators) to returnAsyncGenerator[ScrapeResult, None]directly. Added defaultpages=1toAlbum.get_photos()and removedProcessPoolExecutoroverhead. FixedShort.get_video()by removing invalid@propertydecorator from the async method. UpdatedVideo.likestype annotation toint | str | None. Consolidated HLS download handling via_download_hls. Streamlined HTML parsing extractors with Lexbor CSS selectors and URL deduplication. Refactored CLI URL handler dispatch and argument options. Announced native MCP documentation server support.5553a68/56578f3— Removed the wrapper's hard-coded User-Agent from default headers and replaced constructor-triggered background login with the awaitedClient.create(..., login=True)factory method, setting defaultcore=Noneto instantiate independent networking cores.50f41e3/8cfac95— Corrected aspect ratio calculation inestimate_widthfor HLS variants providing height without width.
5.4.1 — 2026-08-13
fe1d38f— Added the HTML-backedUserHelper.namefield with selectors for both supported profile layouts.
5.4 — 2026-08-11
68f6a65/b16c2ea— Sanitized Short, GIF, and Video download titles to prevent path traversal and illegal filenames, then released 5.4.6a5c064— Added genericScrapeStream/ScrapeResulttyping and the PEP 561py.typedmarker; iterator retry fields remain unset so they resolve fromRuntimeConfig.
5.3.1 fixes — 2026-08-10
1acf957— Made iterator source loading opt-in by default, fixed profile/videosURL construction, separated upload/model/playlist extractors for richer structured results and deduplication, tolerated missing playlist descriptions, and corrected count parsing (issues #96, #97, and #98).
5.3.1 iterator synchronization — 2026-08-08
69bffd6— Consolidated iterator concurrency, ordering, source loading, retry, and error controls intoIteratorConfig.
5.3.1 v4 migration — 2026-08-07
7142475— Migrated to the eaf_base_api v4 request and scraping model:fetch_text()/request(), structured exceptions, source-aware media loading, typed scrape streams/results, and bounded retries.
5.3.1 — 2026-08-06
f93c3eb— FixedVideo.author(load_html), corrected the CLI entry point, and released the 5.3.1 patch.
Supported Platforms
| Platform | Architecture | Status |
|---|---|---|
| Windows 11 | x64 | ✅ Tested |
| macOS Sequoia | x86_64 | ✅ Tested |
| Linux (Arch) | x86_64 | ✅ Tested |
| Android 16 | aarch64 | ✅ Tested |