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-xvideos
For TS → MP4 remuxing support (recommended), install with the optional av dependency:
pip install unofficial-api-for-xvideos[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 xvideos_api import Client
async def main():
client = Client()
# Fetch a video
video = await client.get_video("https://www.xvideos.com/video...")
# Access metadata
print(video.title)
print(video.views)
print(video.likes)
# Download the video
from xvideos_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. Pass a custom BaseCore to configure its singular proxy, bounded request attempts, timeouts, and other networking behavior.
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 xvideos_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 and provides methods to fetch videos, search, and access channels/pornstars.
Methods
Fetch Video get_video()
Video object.Parameters
- url str — The full XVideos video URL
- load_html bool — If
True(default), fetches and parses the HTML page for full metadata
Returns
→ VideoSearch Videos search()
Parameters
- query str — Search terms
- pages int | "all" — Number of result pages or
"all"for automatic iteration - iterator_config IteratorConfig | None — Optional v4 concurrency, ordering, eager-source, retry, and error-handling policy. The package default eagerly loads
html.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Playlist get_playlist()
Parameters
- url str — The playlist URL
- pages int — Number of pages to scrape
- iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's
htmlsource.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Fetch Pornstar Profile get_pornstar()
Returns
→ PornstarFetch Channel Profile get_channel()
Returns
→ ChannelGet Account get_account()
Account instance for authenticated actions using cookies. See Account.Returns
→ AccountVideo
dataclass Inherits from BaseMedia. Represents a single video with all its metadata.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | The video page URL |
title | str | None | Video title |
description | str | None | Video description |
thumbnail_url | str | None | Thumbnail image URL |
preview_video_url | str | None | Short preview clip URL |
publish_date | str | None | Upload date |
content_url | str | None | Direct content URL |
tags | list | None | List of tag strings |
views | str | None | View count |
likes | str | None | Like count |
dislikes | str | None | Dislike count |
rating_votes | str | None | Total rating votes |
comment_count | str | None | Comment count |
author_link | str | None | Uploader profile URL |
length | str | None | Video duration |
pornstars_urls | list | None | URLs of pornstars featured |
embed_url | str | None | Embeddable iframe URL |
cdn_url | str | None | Optional CDN URL carried by listing data |
m3u8_base_url | str | None | HLS master playlist URL |
video_id | str | None | Internal video ID |
Methods
Download Video download()
no_title=True.Parameters
- configuration DownloadConfigHLS — Download settings (quality, path, etc.). See Downloading.
Returns
→ bool | DownloadReportGet Author Channel get_author
Channel object.Returns
→ Channel | NoneGet Performers get_pornstars
Pornstar objects for each featured performer.Returns
→ AsyncGenerator[Pornstar, None]BaseMedia. Accessing an unresolved attribute raises DataNotLoadedError. Use await media.load_sources("html") to load a complete source or await media.load_fields(...) for selected fields. By default, client.get_video(..., load_html=True) calls load_sources("html") and populates the HTML-backed attributes.
Channel
dataclass Inherits from BaseChannelPornstar → BaseMedia. Represents a channel profile.
Attributes
| Attribute | Type | Description |
|---|---|---|
url | str | Channel profile URL |
name | str | None | Channel name |
thumbnail_url | str | None | Profile picture URL |
total_videos | int | None | Number of uploaded videos |
per_page | int | None | Number of videos returned per listing page |
total_pages | int | None | Calculated number of video pages |
profile_hits | str | None | Total profile views |
subscribers | str | None | Subscriber count |
total_videos_views | str | None | Total views across all videos |
signed_up | str | None | Registration date |
last_activity | str | None | Last activity date |
worked_for_with_links | list | None | Links to associated studios/channels |
Methods
Get Channel Videos videos()
pages=0 (default) to fetch all pages.Parameters
- pages int — Maximum pages;
0uses the profile's full page count. - iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's
htmlsource.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Related Channels worked_for_with()
Returns
→ list[Channel]Pornstar
dataclass Inherits from BaseChannelPornstar. Represents a performer profile. Has all Channel attributes plus:
| Extra Attribute | Type | Description |
|---|---|---|
gender | str | None | Gender |
age | str | None | Age |
video_tags | str | None | Most common tags |
Methods
Get Performer Videos videos()
pages=0 (default) to fetch all pages.Parameters
- pages int — Maximum pages;
0uses the profile's full page count. - iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's
htmlsource.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Related Co-workers/Studios worked_for_with()
Returns
→ list[Channel]Account
Provides access to authenticated endpoints. Requires login cookies.
# Provide cookies from your browser session
my_cookies = {
"session_token": "<your_token>",
"session_token_auth": "<your_auth_token>",
}
account = client.get_account(cookies=my_cookies)
# Get your liked videos
async for result in account.get_liked_videos(pages=3):
if result.succeeded:
print(result.unwrap().title)
These authenticated listing endpoints make the required page requests with POST internally. Callers pass only the public IteratorConfig controls and should not set private request-method fields.
Methods
Get Liked Videos get_liked_videos()
POST request is handled internally.Parameters
- pages int — Number of authenticated listing pages.
- iterator_config IteratorConfig | None — Optional public v4 iterator policy; account transport details remain internal.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Recommended Videos get_recommended_videos()
POST request is handled internally.Parameters
- pages int — Number of authenticated listing pages.
- iterator_config IteratorConfig | None — Optional public v4 iterator policy; account transport details remain internal.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Get Watch Later Videos get_watch_later_videos()
POST request is handled internally.Parameters
- pages int — Number of authenticated listing pages.
- iterator_config IteratorConfig | None — Optional public v4 iterator policy; account transport details remain internal.
Returns
→ AsyncGenerator[ScrapeResult[Video], None]Search & Filtering
Use the client.search() method with the sorting enums for powerful filtering:
from xvideos_api.modules.sorting import Sort, SortDate, SortVideoTime, SortQuality
async for result in client.search(
query="example search",
sorting_sort=Sort.Sort_rating,
sorting_date=SortDate.Sort_month,
sorting_time=SortVideoTime.Sort_long,
sort_quality=SortQuality.Sort_1080_plus,
pages=5,
):
if result.succeeded:
video = result.unwrap()
print(f"{video.title}")
Sorting Enums
Sort (Relevance)
| Enum Value | Description |
|---|---|
Sort.Sort_relevance | Most relevant (default) |
Sort.Sort_upload_date | Newest first |
Sort.Sort_rating | Highest rated |
Sort.Sort_length | Longest first |
Sort.Sort_views | Most viewed |
Sort.Sort_random | Random order |
SortDate (Time Period)
| Enum Value | Description |
|---|---|
SortDate.Sort_all | All time (default) |
SortDate.Sort_last_3_days | Last 3 days |
SortDate.Sort_week | This week |
SortDate.Sort_month | This month |
SortDate.Sort_last_3_months | Last 3 months |
SortDate.Sort_last_6_months | Last 6 months |
SortVideoTime (Duration)
| Enum Value | Description |
|---|---|
SortVideoTime.Sort_all | Any duration (default) |
SortVideoTime.Sort_short | 1–3 minutes |
SortVideoTime.Sort_middle | 3–10 minutes |
SortVideoTime.Sort_long | 10+ minutes |
SortVideoTime.Sort_long_10_20min | 10–20 minutes |
SortVideoTime.Sort_really_long | 20+ minutes |
SortQuality (Resolution)
| Enum Value | Description |
|---|---|
SortQuality.Sort_all | Any quality (default) |
SortQuality.Sort_720p | 720p and above |
SortQuality.Sort_1080_plus | 1080p and above |
Downloading
All downloads are configured using the DownloadConfigHLS dataclass:
from xvideos_api import DownloadConfigHLS
config = DownloadConfigHLS(
quality="best", # "best", "half", "worst", or int like 720
path="./downloads", # Output directory (title auto-appended)
no_title=False, # If True, use path as exact filename
remux=True, # Convert TS segments to MP4 (needs av)
return_report=True, # Return DownloadReport instead of bool
)
report = await video.download(configuration=config)
print(report.status) # "completed", "failed", or "cancelled"
print(report.downloaded) # Segments downloaded
print(report.total) # Total segments
DownloadConfigHLS — Full Options
| Parameter | Type | Default | Description |
|---|---|---|---|
quality | str | int | — | Required. "best", "half", "worst", or pixel height (720, 1080, etc.) |
path | str | "./" | Output directory or exact file path |
no_title | bool | False | Skip auto-appending video title to filename |
callback | (int, int) → None | None | Progress callback (downloaded, total). Falls back to text progress bar. |
stop_event | asyncio.Event | None | Set this event to cancel the download |
remux | bool | False | Remux TS → MP4 using PyAV |
start_segment | int | 0 | Skip first N segments |
segment_state_path | str | None | None | Path for resume state JSON file |
segment_dir | str | None | None | Directory for individual segment files |
return_report | bool | False | Return DownloadReport instead of bool |
cleanup_on_stop | bool | True | Delete temp files when cancelled |
keep_segment_dir | bool | False | Keep segment files after completion |
ios_support | bool | False | Restrict audio codec to AAC-only for iOS |
Pagination & Iterators
Methods like search(), get_playlist(), and channel.videos() return async generators that yield typed ScrapeResult[Video] 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("query", pages=3):
if result.succeeded:
video = result.unwrap()
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 | Video | None | The populated video 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. XVideos' 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. Fully populated results require the html source.
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.
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(
"query", pages=3, iterator_config=iterator_config
):
if result.succeeded:
print(result.unwrap().title)
else:
print(result.stage, result.error)
Authenticated account iterators also accept iterator_config; their required POST request is selected internally, so do not configure private transport fields.
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
Source loaders translate request failures into exceptions from xvideos_api.modules.errors. 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 may still 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.
NoLoginCookies now derives from the shared ScraperException; its existing provider import and msg attribute remain available.
See Logging & Cleanup for application logging setup.
| Exception | When Raised |
|---|---|
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 |
NoLoginCookies | Account accessed without cookies set |
from base_api import MediaLoadError
from xvideos_api.modules.errors import NotFound, BotDetection
try:
video = await client.get_video(url)
except MediaLoadError as error:
if isinstance(error.original_error, NotFound):
print("Video does not exist")
elif isinstance(error.original_error, BotDetection):
print("Bot protection triggered — try a proxy")
else:
raise
Changelog
2.5 — 2026-09-15
23dec61/bb96133/d03b382— Adopted shared request and download error handling frombase_api.modules.provider(requiringeaf-base-api>=4.2.0). Failed downloads now raiseDownloadFailedwith complete diagnostic context. Modernized HTML scraping forVideo,Channel, andPornstarclasses with layout anchors and resilient fallback selectors (JSON-LD, setVideoTitle, setThumbUrl, setVideoHLS, data-id, model links). Added comprehensive unit test suites for channel, pornstar, and video extraction.
2.5 — 2026-08-11
c1e34b3/f8ebcb3— Added genericScrapeResult[Video]typing and the PEP 561py.typedmarker, left iterator retry policies unset so they resolve fromRuntimeConfig, and released 2.5.
2.4 — 2026-08-08
9a88d09— Consolidated account, profile, search, and playlist iterator concurrency, ordering, source loading, retry, and error controls intoIteratorConfig; authenticated account listings use their requiredPOSTtransport internally.
2.4 migration — 2026-08-07
e39c5ec— 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.
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.
XVideos API includes a command-line interface accessible via xvideos_api or python -m xvideos_api:
# Download a single video
xvideos_api --download "https://www.xvideos.com/video..." --quality best --output ./video.mp4 --no-title True
# Download from a file of URLs
xvideos_api --file urls.txt --quality 720 --output ./downloads/ --no-title False
CLI Options
| Flag | Description |
|---|---|
--download URL | Video URL to download |
--file PATH | Text file with URLs (one per line) |
--quality | Video quality: best, half, worst |
--output | Output path (directory or file) |
--no-title | True/False — Skip auto-appending title |
Supported Platforms
| Platform | Architecture | Status |
|---|---|---|
| Windows 11 | x64 | ✅ Tested |
| macOS Sequoia | x86_64 | ✅ Tested |
| Linux (Arch) | x86_64 | ✅ Tested |
| Android 16 | aarch64 | ✅ Tested |
remux=False on those platforms.