Python Async v1.7.1

XFreeHD API

A fully asynchronous Python API wrapper and scraper for XFreeHD. Fetch video metadata, photo albums, execute search queries, and download media files directly from CDNs. 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-xfreehd
Note
Requires Python ≥ 3.12 and eaf-base-api>=4.0.0. The compatible networking core is installed automatically.

🚀 Quick Start

All operations are asynchronous and should run within an async context:

python
import asyncio
from xfreehd_api import Client
from base_api import DownloadConfigRAW

async def main():
    client = Client()

    # Fetch a video
    video = await client.get_video("https://xfreehd.com/video/12345/example-video")

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

    # Download video using RAW downloader (direct MP4)
    config = DownloadConfigRAW(quality="hd", path="./downloads")
    await video.download(configuration=config)

    # Fetch an album and get image links
    album = await client.get_album("https://xfreehd.com/album/12345/example-album")
    images = await album.get_all_images()
    print(f"Found {len(images)} images in album.")

asyncio.run(main())

⚙️ Configuration

XFreeHD API uses eaf_base_api 4.x for networking. You can configure a proxy, timeouts, request attempts, limits, and HTTP parameters via a custom BaseCore.

Please refer to the eaf_base_api Documentation for the complete reference on how to set up RuntimeConfig and BaseCore.

python
from base_api import BaseCore
from base_api.modules.config import RuntimeConfig
from xfreehd_api import Client

my_config = RuntimeConfig()
my_config.proxy = "socks5://127.0.0.1:9050"
my_config.request_attempts = 4

core = BaseCore(configuration=my_config)
client = Client(core=core)

IteratorConfig, retry policy, and custom scrape errors

Iterator methods take one iterator_config object. Keep load_specific_sources=("html",) when replacing XFreeHD's defaults so full Video metadata is loaded.

python
from base_api import ErrorAction, ErrorMode, ResultOrder, RetryPolicy, ScrapeErrorContext
from base_api.modules.config import IteratorConfig

async def handle_scrape_error(context: ScrapeErrorContext) -> ErrorAction:
    print(context.stage, context.url, context.error, context.attempt)
    return ErrorAction.RETRY

retry = RetryPolicy(
    max_attempts=3, base_delay=0.5, multiplier=2, max_delay=8, jitter=0.25
)
iterator_config = IteratorConfig(
    max_page_concurrency=2,
    max_item_concurrency=5,
    order=ResultOrder.ORIGINAL,
    load_specific_sources=("html",),
    page_retry=retry,
    item_retry=retry,
    page_error_mode=ErrorMode.SKIP,
    item_error_mode=ErrorMode.YIELD,
    page_error_handler=handle_scrape_error,
    item_error_handler=handle_scrape_error,
)

async for result in client.search("beach", iterator_config=iterator_config):
    print(result.unwrap().title if result.succeeded else result.error)

max_attempts includes the first attempt. page_error_handler and item_error_handler are routed independently. This example shares one callable because it intentionally applies the same retry decision to both stages; use separate callables when page and item policy differ.


🔌 Client

The main class to interact with the XFreeHD scraper ecosystem.

python
from xfreehd_api import Client
from base_api import BaseCore

client = Client()
client_custom = Client(core=BaseCore())

Constructor Parameters

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

Methods

Fetch Video get_video()

async
Fetches metadata for a video URL and returns a populated Video object.
await client.get_video( url: str, load_html: bool = True ) -> Video

Parameters

  • url str — The XFreeHD video page URL
  • load_html bool — If True (default), fetches and extracts metadata immediately

Returns

→ Video

Fetch Album get_album()

async
Fetches photo album information and returns a populated Album object.
await client.get_album( url: str, load_html: bool = True ) -> Album

Parameters

  • url str — The XFreeHD photo album page URL
  • load_html bool — If True (default), fetches and extracts metadata immediately

Returns

→ Album

Search Videos search()

async
Queries XFreeHD search and streams video results via an asynchronous generator.
async for result in client.search( query: str, pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search keywords
  • pages int — Number of search pages to parse (default: 5)
  • iterator_config IteratorConfig | None — Per-iterator concurrency, source loading, ordering, retry, error-mode, and custom-handler settings. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with details extracted from player page HTML elements.

Attributes

AttributeTypeDescription
urlstrThe video page URL
titlestr | NoneVideo title
likesstr | NoneNumber of likes
dislikesstr | NoneNumber of dislikes
publish_datestr | NonePublication / upload date string
viewsstr | NoneNumber of views
authorstr | NoneUsername of the video uploader
author_thumbnailstr | NoneUploader avatar picture URL
author_urlstr | NoneUploader channel profile URL
author_subscribersstr | NoneUploader subscriber count
thumbnailstr | NoneCover image thumbnail URL
lengthstr | NoneDuration string (e.g. 12:34)
categorieslist[str] | NoneList of category labels
tagslist[str] | NoneList of video tags
cdn_urlslist[str] | NoneDirect MP4 CDN paths (up to two qualities: SD, HD)
embed_urlstr | NoneEmbeddable iframe player URL
ratingstr | NoneRatings metadata loaded from HTML

Methods

Video Qualities video_qualities()

Returns the quality tiers inferred from the number of CDN URLs: one URL maps to 480p and two map to 480p/720p. Other counts currently return an empty list.
video.video_qualities() -> list[int]

Returns

→ list[int]

Download Video download()

async
Downloads the video directly from CDN server using DownloadConfigRAW. Supports hd and sd quality configuration.

Video.download() returns True when the RAW downloader completes. On an exception it raises DownloadFailed with the video URL and original cause; it no longer returns an exception object. Replace checks such as isinstance(result, DownloadFailed) with try/except DownloadFailed.

await video.download( configuration: DownloadConfigRAW ) -> bool

Parameters

  • configuration DownloadConfigRAW — RAW download options. Set quality="hd" or quality="sd". See Downloading Options.

Returns

→ bool

🖼️ Album

dataclass Inherits from BaseMedia. Represents a picture gallery album with support for paginated photo fetching.

Attributes

AttributeTypeDescription
urlstrAlbum page URL
titlestr | NoneAlbum title
total_pages_countint | NoneCalculated total pages in the album

Methods

Get Images by Page get_images_by_page()

async
Fetches direct photo image URLs for a specific album index page.

get_images_by_page(page) raises NotFound when page exceeds total_pages_count; the message includes the requested page, album URL, and total page count.

await album.get_images_by_page( page: int = 1 ) -> list[str]

Parameters

  • page int — Page index (1-based, default: 1)

Returns

→ list[str]

Get All Images get_all_images()

async
Iterates and collects direct image URLs for all pages in this album concurrently.
await album.get_all_images() -> list[str]

Returns

→ list[str]

⬇️ Downloading Options

Video.download() returns True when the RAW downloader completes. On an exception it raises DownloadFailed with the video URL and original cause; it no longer returns an exception object. Replace checks such as isinstance(result, DownloadFailed) with try/except DownloadFailed.

XFreeHD video files are direct MP4 assets on CDNs. Configure downloads using DownloadConfigRAW:

python
from base_api import DownloadConfigRAW

config = DownloadConfigRAW(
    quality="hd",             # Set to "hd" for high quality, or "sd" for standard quality
    path="./downloads",        # Destination path
    no_title=False,            # If False, automatically appends title + ".mp4"
    allow_multipart=True,      # Enable parallel segmented downloads using range requests
    max_workers=5              # Concurrent download segment threads
)

success = await video.download(configuration=config)

For full details on RAW download parameters, see the reference in eaf_base_api Documentation.

📊 Scraping Results

Iterating methods like client.search() return async generators yielding ScrapeResult instances:

python
async for result in client.search("beach", pages=2):
    if result.succeeded:
        video = result.unwrap()     # The Video object
        print(video.title)
    else:
        print(f"{result.stage} failed: {result.error}")

ScrapeResult Attributes

AttributeTypeDescription
succeededboolWhether the scraping task succeeded.
unwrap()VideoReturns the parsed video, or raises the stored error.
itemVideo | NoneThe parsed video when successful.
errorScrapeOperationError | NoneThe typed page or item failure.
stageScrapeStageThe iterator stage: PAGE or ITEM.

⚠️ Error Handling

Source loaders translate request failures into exceptions from xfreehd_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.

See Logging & Cleanup for application logging setup.

ExceptionTrigger Cause
NotFoundServer returned HTTP 404 (e.g. video/album deleted)
NetworkErrorRequest failed due to a network error, exhausted request retries, or a non-404 HTTPStatusError
BotDetectionBot-protection challenge block detected
ProxyErrorProxy configuration failed or proxy is down
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 xfreehd_api.modules.errors import NotFound, BotDetection

try:
    video = await client.get_video(url)
except MediaLoadError as error:
    if isinstance(error.original_error, NotFound):
        print("This video does not exist!")
    elif isinstance(error.original_error, BotDetection):
        print("Scraper was blocked by anti-bot measures.")
    else:
        raise

📝 Changelog

1.7.1 — 2026-09-15

  • a728c0e / b18b650 / fb5e92c — Adopted shared request and download error handling from base_api.modules.provider (requiring eaf-base-api>=4.2.0). Failed downloads now raise DownloadFailed with complete diagnostic context. Expanded Video metadata with author_thumbnail, author_url, author_subscribers, embed_url, and rating. Cleanly report removed or unavailable videos via ResourceGone. Added robust fallback selectors and offline extraction unit tests.

1.7.1 — 2026-08-14 264b618

  • Added Video.video_qualities() for consumers that need numeric quality tiers for the current one- or two-CDN layouts.

1.7 — 2026-08-11 d41c536

  • Added the py.typed marker and changed unset iterator retry policies to resolve from the live RuntimeConfig.
  • Updated the package version from 1.6 to 1.7.

IteratorConfig synchronization — 2026-08-08 5f55f07

  • Replaced the legacy per-call iterator arguments with IteratorConfig.
  • Preserved the required html source loading for search results.

Core v4 migration — 2026-08-07 bf40a03

  • Migrated to eaf-base-api 4.x with explicit request methods and source-aware media loading.
  • Added bounded retries, structured errors, extractor validation, improved concurrency, and deterministic stream cleanup.
  • Removed obsolete compatibility code and adopted the current ScrapeResult model.

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

XFreeHD API includes a command-line interface accessible via xfreehd_api or python -m xfreehd_api:

bash
# Download a single video
xfreehd_api --download "https://www.xfreehd.com/video/12345/sample" --quality best --output ./downloads --no-title False

# Or invoke via python -m
python -m xfreehd_api --download "https://www.xfreehd.com/video/12345/sample" --quality best --output ./downloads --no-title False

# Download from a line-separated file of URLs
xfreehd_api --file urls.txt --quality best --output ./downloads --no-title False

CLI Options

FlagDescription
--download URLVideo URL to download
--file FILEText file with URLs (separated by newlines)
--quality QUALITYVideo quality: best, half, worst
--output DIRDestination directory or output file path
--no-title True/FalseSkip auto-appending video title to output filename (default: False)

🖥️ Supported Platforms

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