Python Async v2.4.1

XNXX API

A fully asynchronous Python API wrapper and scraper for XNXX. Fetch videos, metadata, search with complex filters, and iterate over user uploads — all powered by the eaf_base_api networking engine.

GitHub
⚡ Recommended AI Workflow Official MCP Server: https://mcp.echteralsfake.me/mcp

Supercharge your development by connecting your AI coding assistant (Cursor, VS Code / Copilot, Claude Desktop, Windsurf, Zed, Antigravity). No authentication required · No strict rate limits · Covers all 16 APIs · Always up to date · Completely anonymous (0 logs, no profiling). Connecting this MCP server to your AI assistant is absolutely the recommended way to work on and build with these APIs.

⚠️ Legal Disclaimer
This tool is an unofficial, independent project and is not affiliated with, endorsed by, or sponsored by the target website. This software is provided "as is" for educational and personal purposes only. The developer assumes no responsibility for any consequences arising from the use of this tool, including but not limited to account suspension, IP blocking, or any violation of the target website's Terms of Service. Users are solely responsible for ensuring their use complies with all applicable laws and policies. Use at your own risk.
💚 Support & Commercial Licensing
If you find this project helpful, please consider donating to support its continued development!

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:

bash
pip install unofficial-api-for-xnxx

For TS→MP4 remuxing support (recommended for HLS downloads), install with the optional av dependency:

bash
pip install unofficial-api-for-xnxx[av]
Note
Requires Python ≥ 3.12. 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:

python
import asyncio
from xnxx_api import Client

async def main():
    client = Client()

    # Fetch a video
    video = await client.get_video("https://www.xnxx.com/video-...")

    # Access metadata
    print(video.title)
    print(video.length)
    print(video.views)

    # 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. 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.

python
from base_api import BaseCore
from base_api.modules.config import RuntimeConfig
from xnxx_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 the entry point for all API requests. It initializes and manages sessions to retrieve videos, fetch user data, and execute paginated search queries.

python
from xnxx_api import Client
from base_api import BaseCore

client = Client()

# Or initialize with custom core config
client_custom = Client(core=BaseCore())

Constructor Parameters

  • core BaseCore — Networking core instance (default: BaseCore())

Methods

Fetch Video get_video()

async
Fetches a video page and returns a populated Video object. Extracts inline JSON metadata and master HLS playlist files.
await client.get_video( url: str, load_html: bool = True ) -> Video

Parameters

  • url str — The full XNXX video URL
  • load_html bool — If True (default), fetches and parses the HTML page for metadata

Returns

→ Video

Fetch User get_user()

async
Fetches a user profile page and returns a populated User object. Simultaneously requests the profile page and the initial video JSON listing page to fetch totals.
await client.get_user( url: str, load_html: bool = True ) -> User

Parameters

  • url str — The full XNXX user profile URL
  • load_html bool — If True (default), parses HTML pages

Returns

→ User

Search Videos search_videos()

async
Searches for videos matching the query text. Supports filtering by search mode, duration range, upload date, and resolution quality. Yields results paginated. See Search & Filtering.
async for result in client.search_videos( query: str, pages: int = 0, mode: Mode | str = "", upload_time: UploadTime | str = "", length: Length | str = "", searching_quality: SearchingQuality | str = "", iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search keywords
  • pages int — Number of pages to iterate
  • mode Mode | str — Sort mode (e.g. Mode.hits, Mode.random)
  • upload_time UploadTime | str — Upload age filter (e.g. UploadTime.month)
  • length Length | str — Video duration category (e.g. Length.X_10min_plus)
  • searching_quality SearchingQuality | str — Video quality category (e.g. SearchingQuality.X_1080p_plus)
  • iterator_config IteratorConfig | None — Optional v4 concurrency, ordering, eager-source, retry, and error-handling policy. The package default eagerly loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with parsed metadata and HLS download streams.

Attributes

AttributeTypeDescription
urlstrThe video page URL
titlestr | NoneVideo title
descriptionstr | NoneVideo description metadata
thumbnailstr | NoneThumbnail cover image URL
publish_datestr | NoneUpload / publish date string
lengthstr | NoneDuration string (e.g. PT15M42S)
m3u8_base_urlstr | NoneMaster HLS stream playlist URL
viewsstr | NoneTotal view count
authorstr | NoneUploader name shown on the video page
tagslist[str] | NoneKeyword tags linked from the video page
Scraper Iterator Fallback Attributes
video_idstr | NoneNumeric video identifier
video_eidstr | NoneExternal unique string ID
preview_video_urlstr | NoneDirect URL to the short trailer / preview video clip
ratingstr | NoneUpvote percentage or ranking score
max_qualitystr | NoneMaximum resolution tag badge

Methods

Download Video download()

async
Downloads the video via HLS streaming. Appends the video title to the output path unless no_title=True on the config. Ordinary failures raise DownloadFailed with full diagnostic context and chained cause.
await video.download( configuration: DownloadConfigHLS ) -> bool | DownloadReport

Parameters

Returns

→ bool | DownloadReport

🧑 User

dataclass Inherits from BaseMedia. Represents an XNXX user profile containing upload statistics and video iterations.

Attributes

AttributeTypeDescription
urlstrThe user profile URL
total_videos_countint | NoneTotal uploaded videos
total_pages_countint | NoneTotal number of paginated video list pages
total_videos_viewsstr | NoneTotal views across all uploads

Methods

Get Uploaded Videos videos()

async
Asynchronously iterates over videos uploaded by the user, navigating page-by-page.
async for result in user.videos( pages: int = 0, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Max pages to fetch (will automatically cap to total_pages_count)
  • iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🔍 Search & Filtering

XNXX video searches use customized StrEnums for constructing query filters. Import them from xnxx_api.modules.search_filters:

python
from xnxx_api.modules.search_filters import Length, UploadTime, SearchingQuality, Mode

async for result in client.search_videos(
    query="college",
    pages=3,
    mode=Mode.hits,
    length=Length.X_10min_plus,
    searching_quality=SearchingQuality.X_1080p_plus,
    upload_time=UploadTime.month
):
    if result.succeeded:
        video = result.unwrap()
        print(video.title)

Filter Options

Sort Mode (Mode)

Enum MemberPath SuffixDescription
Mode.default""Standard relevant sort
Mode.hits"/hits"Sort by total hits / views
Mode.random"/random"Randomized ordering

Duration Length (Length)

Enum MemberPath SuffixRange
Length.X_0_10min"/0-10min"Under 10 minutes
Length.X_10min_plus"/10min+"10 minutes and longer
Length.X_10_20min"/10-20min"Between 10 and 20 minutes
Length.X_20min_plus"/20min+"20 minutes and longer

Upload Time Age (UploadTime)

Enum MemberPath SuffixTimeline
UploadTime.month"/month"Uploaded within this month
UploadTime.year"/year"Uploaded within this year

Quality Class (SearchingQuality)

Enum MemberPath SuffixTarget Quality
SearchingQuality.X_720p"/hd-only"HD resolutions (720p)
SearchingQuality.X_1080p_plus"/fullhd"Full HD & UHD (1080p and higher)

⬇️ Downloading Options

XNXX video downloads are HLS-only. Configure stream downloads via DownloadConfigHLS:

python
from base_api import DownloadConfigHLS

config = DownloadConfigHLS(
    quality="best",            # "best", "half", "worst", or height int
    path="./downloads",        # Destination path
    no_title=False,            # If False, automatically appends title + ".mp4"
)

success = await video.download(configuration=config)

For full details on download options and setup configurations, see the eaf_base_api Documentation.

📄 Pagination & Iterators

Page scraper lists (client.search_videos() and user.videos()) yield typed ScrapeResult[Video] values. Use succeeded to branch safely or unwrap() to return the item and raise its terminal error on failure:

python
async for result in client.search_videos("amateur", pages=2):
    if result.succeeded:
        video = result.unwrap()
        print(video.title, video.url)
    else:
        print(result.stage, result.url, result.error)

ScrapeResult Properties

AttributeTypeDescription
stageScrapeStageWhether the result came from the page or item stage
urlstrThe video item page URL
page_indexintZero-based source page index
item_indexint | NoneZero-based item index, or None for a page failure
attemptsintNumber of attempts used by the yielding stage
itemVideo | NonePopulated video on success
errorScrapeOperationError | NoneTyped terminal page or item error on failure
succeededboolTrue when error is None

IteratorConfig, bounded retries, and custom handlers

All iterator-only controls now live in one IteratorConfig. XNXX'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. Results need the html source for eager population.

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.

python
from base_api import ErrorAction, 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,
    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(
    "amateur", 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.

Independent page and item handlers

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 xnxx_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.

RegionBlocked now derives from the shared ScraperException; its existing provider import and msg attribute remain available.

See Logging & Cleanup for application logging setup.

ExceptionTrigger Cause
NotFoundServer returned HTTP 404
RegionBlockedThe base request raised AccessDeniedError (for example, HTTP 401 or 403). This does not by itself prove a geographic restriction.
NetworkErrorRequest failed due to a network error, exhausted request retries, or a non-404 HTTPStatusError
BotDetectionBot-protection challenge block detected
ProxyErrorProxy connection failed
UnknownNetworkErrorUnexpected network errors
DownloadFailedDownload preparation or transfer failed; the video URL is included and __cause__ retains the original exception
python
from base_api import MediaLoadError
from xnxx_api.modules.errors import RegionBlocked

try:
    video = await client.get_video(url)
except MediaLoadError as error:
    if isinstance(error.original_error, RegionBlocked):
        print("Access to this URL was denied; inspect the original error.")
    else:
        raise

💻 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 XNXX API package includes a CLI tool executable directly from the terminal via xnxx_api or python -m xnxx_api:

bash
# Download a single video to target folder
xnxx_api --download "https://www.xnxx.com/video-..." --quality best --output ./downloads --no-title False

# Batch download URLs from a line-separated text file
xnxx_api --file file_list.txt --quality best --output ./downloads --no-title False

CLI Parameters

FlagDescription
--download URLDownload video from the specified XNXX URL
--file PATHRead URLs from a text file (separated by newlines) and download them
--quality QUALITYRequired. Set video download quality: best, half, worst
--output DIRRequired. Destination file or directory path
--no-title True/FalseRequired. Skip auto-appending video title to output filename (set to True if output path includes target filename)

📝 Changelog

2.4.1 — 2026-09-15

  • 6857235 / 0ee8040 — Adopted shared request and download error handling with base_api.modules.provider (requiring eaf-base-api>=4.2.0). Failed downloads now raise DownloadFailed with complete context. Added layout anchor validations to detect page structure changes. Implemented robust fallback parsing for ld+json and HTML metadata (title, description, thumbnail, publish date, length, views, author, tags). Cleaned up log messages when paginating user videos.

2.4.1 — 2026-08-14

  • af53c16 — Added HTML-backed Video.author and Video.tags fields.

2.4 — 2026-08-11

  • 1d57554 — Prefixed search-result links with https://xnxx.com, added generic ScrapeResult[Video] typing and the PEP 561 py.typed marker, left iterator retry policies unset so they resolve from RuntimeConfig, and released 2.4.

2.3 — 2026-08-08

  • 45c4e90 — Consolidated User.videos() and Client.search_videos() pagination, concurrency, source loading, retry, ordering, and error controls into IteratorConfig.

2.3 migration — 2026-08-07

  • 0bf61e5 — 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.

🖥️ Supported Platforms

PlatformArchitectureStatus
Windows 11x64✅ Tested
macOS Sequoiax86_64✅ Tested
Linux (Arch)x86_64✅ Tested
Android 16aarch64✅ Tested