Python Async v2.6.2

xHamster API

A fully asynchronous Python API wrapper and scraper for xHamster. Fetch video details, shorts, channel information, pornstar metadata, or log into user accounts. Stream downloads via HLS. 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-xhamster

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

bash
pip install unofficial-api-for-xhamster[av]
Note
Requires Python ≥ 3.12. Version 2.6.2 uses eaf_base_api ≥ 4.0.0, which is installed automatically.

🚀 Quick Start

Every operation in this wrapper is asynchronous. Run your code inside an async main function:

python
import asyncio
from xhamster_api import Client
from base_api import DownloadConfigHLS

async def main():
    client = Client()

    # Fetch video metadata
    video = await client.get_video("https://xhamster.com/videos/example-video-123")
    print(video.title)
    print(video.uploader_name)

    # Download video via HLS streaming
    config = DownloadConfigHLS(quality="best", path="./downloads")
    await video.download(configuration=config)

asyncio.run(main())

⚙️ Configuration

Networking configurations (proxies, timeouts, limits) are defined via RuntimeConfig inside the BaseCore wrapper.

Please refer to the shared eaf_base_api Documentation for the complete reference.

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

my_config = RuntimeConfig()
my_config.proxy = "socks5://127.0.0.1:9050"
my_config.request_attempts = 4
my_config.videos_concurrency = 8
my_config.pages_concurrency = 2

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

🔌 Client

The entry point for retrieving all resources from xHamster.

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

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

Constructor Parameters

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

Methods

Fetch Video get_video()

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

Parameters

  • url str — xHamster video page URL
  • load_html bool — If True, pre-fetches and extracts page metadata immediately

Returns

→ Video

Fetch Pornstar get_pornstar()

async
Loads a pornstar biography and information metadata profile.
await client.get_pornstar( url: str, load_html: bool = True ) -> Pornstar

Parameters

  • url str — The pornstar profile page URL
  • load_html bool — Pre-load parsed properties

Returns

→ Pornstar

Fetch Creator get_creator()

async
Loads a content creator profile page and returns a populated Creator object.
await client.get_creator( url: str, load_html: bool = True ) -> Creator

Parameters

  • url str — The content creator profile page URL
  • load_html bool — Pre-load parsed properties

Returns

→ Creator

Fetch Channel get_channel()

async
Loads a studio or publisher channel profile page.
await client.get_channel( url: str, load_html: bool = True ) -> Channel

Parameters

  • url str — The channel page URL
  • load_html bool — Pre-load parsed properties

Returns

→ Channel

Fetch Short get_short()

async
Fetches a mobile "Short" video page and returns a populated Short object.
await client.get_short( url: str, load_html: bool = True ) -> Short

Parameters

  • url str — The xHamster shorts URL
  • load_html bool — Pre-load parsed properties

Returns

→ Short

Search Videos search_videos()

async
Runs a query through search filters and yields video scrape results.
async for result in client.search_videos( query: str, minimum_quality: Literal["720p", "1080p", "2160p"] = "720p", sort_by: Literal["views", "newest", "best", "longest"] | None = None, category: Literal["german", "amateur", "18-year-old", "granny", "anal", "old-young", "mature", "mom", "milf", "big-tits", "big-natural-tits", "lesbian", "teen", "cum-in-mouth", "bdsm", "porn-for-women", "russian", "vintage", "hairy", "brutal-sex"] | list[str] | None = None, vr: bool = False, full_length_only: bool = False, min_duration: Literal["2", "5", "10", "30", "40"] | None = None, date: Literal["latest", "weekly", "monthly", "yearly"] | None = None, production: Literal["studios", "creators"] | None = None, fps: Literal["30", "60"] | None = None, pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search query words
  • minimum_quality str — Minimum resolution limit (default: "720p")
  • sort_by str — Sort criteria
  • category str | list[str] — Category filters
  • vr bool — Filter for VR videos
  • full_length_only bool — Only include full-length movies
  • min_duration str — Minimum duration limit in minutes
  • date str — Upload age filter (e.g. "weekly")
  • production str — Filter studio or creator-made uploads
  • fps str — Framerate filter
  • pages int — Number of search result index pages to scrape
  • iterator_config IteratorConfig | None — Concurrency, ordering, source loading, retry, and terminal-error policy. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Authenticate Account login()

async
Performs authentication and returns an active Account instance. Validates API response payloads (extras.error / extras.result), raising LoginFailed when credentials are invalid or payload structure is unexpected. Supports cookie injection fallback.
await client.login( username: str, password: str, cookies: dict | None = None ) -> Account

Parameters

  • username str — Account username or email
  • password str — Account password
  • cookies dict | None — Dict of session cookies to bypass authorization steps

Returns

→ Account

🎬 Video

dataclass Inherits from BaseMedia. Represents a standard video with metadata extracted from page source JSON elements.

Attributes

AttributeTypeDescription
urlstrThe video page URL
video_idstr | NoneUnique video key ID
video_hashstr | NoneHashed slug associated with the video ID
titlestr | NoneVideo title
descriptionstr | NoneVideo description
durationint | NoneVideo duration in seconds
viewsint | NoneTotal view count
comments_countint | NoneTotal comment count
created_timestampstr | NoneCreation timestamp supplied by the page data
date_agostr | NoneHuman-readable age of the upload
rating_percentageint | NoneLike percentage value
likesint | NoneNumber of likes
dislikesint | NoneNumber of dislikes
is_vrbool | NoneWhether the video is a VR video
is_hdbool | NoneWhether the video is available in HD
max_resolutionstr | NoneHighest resolution reported by the page data
orientationstr | NoneContent orientation label, such as "straight" or "gay"
uploader_namestr | NoneUploader profile name
uploader_subscribersstr | NoneUploader subscriber count
tagslist[str] | NoneList of tags
categorieslist[str] | NoneList of category labels
pornstarslist[str] | NonePornstars starring in the video
thumbnailstr | NoneThumbnail cover image URL
preview_thumbnailstr | NonePreview sprite or animated thumbnail URL
m3u8_base_urlstr | NoneHLS playlist URL
preview_videostr | NonePreview media path

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

  • configuration DownloadConfigHLS — HLS download configurations

Returns

→ bool | DownloadReport

📱 Short

dataclass Inherits from BaseMedia. Represents a mobile-style short-form video.

Attributes

AttributeTypeDescription
urlstrThe short URL
titlestr | NoneVideo title
tagslist[str] | NoneAssociated tags
thumbnailstr | NoneCover thumbnail URL
video_idstr | NoneUnique short video ID
comment_countstr | NoneNumber of comments
durationstr | NoneDuration string
created_atstr | NoneUpload timestamp string
poster_urlstr | NonePoster photo URL
author_linkstr | NoneLink to author profile
author_logostr | NoneAuthor's avatar path
m3u8_base_urlstr | NoneHLS playlist URL
likesstr | NoneNumber of likes
viewsstr | NoneNumber of views
author_subscribersstr | NoneAuthor's subscriber count
authorstr | NoneName of the author
preview_videostr | NoneShort preview clip path

Methods

Download Short download()

async
Downloads the short via HLS stream.
await short.download( configuration: DownloadConfigHLS ) -> bool | DownloadReport

Parameters

  • configuration DownloadConfigHLS — HLS download configurations

Returns

→ bool | DownloadReport

👤 Pornstar / Creator / Channel

Profiles representing models, creator users, and publisher studios share a common metadata hierarchy.

Attributes

AttributeTypeDescription
urlstrProfile URL
namestr | NoneDisplay name
subscribers_countstr | NoneNumber of subscribers
videos_countstr | NoneTotal uploaded videos
total_views_countstr | NoneSum of views across all videos
avatar_urlstr | NoneAvatar profile image URL
pornstar_informationdict | NoneBiography sidebar parameters (only parsed for Pornstar and Creator models)

Methods

Fetch Profile Videos videos()

async
Yields videos uploaded to this profile page.
async for result in profile.videos( pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Pages to fetch
  • iterator_config IteratorConfig | None — Complete iterator policy. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Fetch Profile Shorts get_shorts()

async
Yields mobile shorts for a Pornstar or Creator. This method is not available on Channel or the shared Something base class.
async for result in pornstar.get_shorts( pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Short], None]

Parameters

  • pages int — Pages to fetch
  • iterator_config IteratorConfig | None — Complete iterator policy for page and Short items.

Returns

→ AsyncGenerator[ScrapeResult[Short], None]

🔒 Account

Provides authenticated scraper operations inside logged-in sessions.

Methods

Get Liked Videos get_liked_videos()

async
Yields videos marked as liked inside the user account.
async for result in account.get_liked_videos( pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Pages to load
  • iterator_config IteratorConfig | None — Complete iterator policy. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Get Playlist Videos get_account_playlist()

async
Yields videos from a specific playlist URL.
async for result in account.get_account_playlist( url: str, pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • url str — xHamster playlist URL (e.g. https://xhamster.com/my/playlists/...)
  • pages int — Pages to load
  • iterator_config IteratorConfig | None — Complete iterator policy. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

⬇️ Downloading Options

xHamster serves videos and shorts via HLS streaming. Configure downloads using DownloadConfigHLS:

python
from base_api import DownloadConfigHLS

config = DownloadConfigHLS(
    quality="best",            # "best", "half", "worst", or height int (e.g. 720)
    path="./downloads",        # Output directory
    no_title=False,            # Auto-appends title + ".mp4" if False
    return_report=True
)

report = await video.download(configuration=config)

For additional downloading properties, see eaf_base_api Documentation.

📊 Scraping Results

Iterators accept one IteratorConfig policy and yield immutable ScrapeResult[Video] or ScrapeResult[Short] values. The package default loads html, skips terminal page failures, and resolves its unset retry policies from the active RuntimeConfig.request_* settings. A supplied configuration replaces that default, so preserve the HTML source and page error mode explicitly; the example below deliberately opts into yielding page failures.

The public configuration fields are max_page_concurrency, max_item_concurrency, max_pending_items, extract_in_thread, order, both *_error_mode, both *_retry, both *_error_handler, load_specific_fields, and load_specific_sources.

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

async def handle_error(context: ScrapeErrorContext) -> ErrorAction:
    if context.attempt < context.max_attempts:
        return ErrorAction.RETRY
    return ErrorAction.YIELD

iterator_config = IteratorConfig(
    max_page_concurrency=2,
    max_item_concurrency=8,
    order=ResultOrder.ORIGINAL,
    load_specific_sources=("html",),
    page_retry=RetryPolicy(max_attempts=4, base_delay=0.5, jitter=0.25),
    item_retry=RetryPolicy(max_attempts=4, base_delay=0.5, jitter=0.25),
    page_error_mode=ErrorMode.YIELD,
    item_error_mode=ErrorMode.YIELD,
    page_error_handler=handle_error,
    item_error_handler=handle_error,
)

async for result in client.search_videos(
    "reality", pages=2, iterator_config=iterator_config
):
    if result.succeeded:
        video = result.unwrap()
        print(video.title)
    else:
        print(result.stage, result.url, result.attempts, result.error)
Independent page and item handlers
Page and item handlers are routed independently. Assign the same callable to both fields, as above, only when both stages should use the same policy. A handler receives ScrapeErrorContext(stage, url, error, attempt, max_attempts, page_index, item_index) and returns ErrorAction.RETRY, RAISE, YIELD, or SKIP. RetryPolicy exposes max_attempts, base_delay, multiplier, max_delay, jitter, and retry_for; attempts include the first call.

ScrapeResult Attributes

AttributeTypeDescription
stageScrapeStagePAGE or ITEM
urlstrThe page or item target URL
page_indexintZero-based source-page index
item_indexint | NoneZero-based item index; None for page failures
attemptsintNumber of attempts consumed
itemVideo | Short | NoneThe loaded item on success
errorScrapeOperationError | NoneThe typed terminal error on failure
succeededboolTrue when the result contains an item
unwrap()Video | ShortReturns the item or raises its terminal error

⚠️ Error Handling

Source loaders translate request failures into exceptions from xhamster_api.modules.errors. Calls that load media expose ordinary loader failures through base_api.MediaLoadError (or MediaLoadErrors for several sources); inspect original_error/errors. Operations outside media loading, such as login, 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.

LoginFailed 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 (e.g. video 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
LoginFailedSign-in credentials, API response error (e.g. invalid login or password in JSON response payload), or API challenge failed
python
from xhamster_api.modules.errors import NotFound, LoginFailed

try:
    account = await client.login(username, password)
except LoginFailed:
    print("Login failed. Please verify credentials.")

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

xHamster API includes a command-line interface accessible via xhamster_api or python -m xhamster_api:

bash
# Download a single video
xhamster_api --download "https://xhamster.com/videos/sample-title-12345" --quality best --output ./downloads --no-title False

# Or invoke via python -m
python -m xhamster_api --download "https://xhamster.com/videos/sample-title-12345" --quality best --output ./downloads --no-title False

# Download from a line-separated file of URLs
xhamster_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

📝 Changelog

DateCommitChanges
2026-09-17e452f99 / 79f61a7Released 2.6.2. Added validation for invalid credential error payloads in xHamster login API JSON responses (extras.error), raising LoginFailed when authentication fails rather than returning an invalid Account instance on HTTP 200 responses.
2026-09-15a136ebe / 8030a56Adopted 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 resilient fallback selectors for video and pornstar metadata. Enhanced download stream URL resolution and error handling. Added snippet-based unit tests for video and pornstar extraction.
2026-08-24c640d89 / b7c302eAdapted listing extraction to the site's new per-video data-video-id layout, searched all candidate result containers, deduplicated overlapping results, and mapped listing duration into the current duration field.
2026-08-1479224f5Released 2.6 with expanded Video metadata: hash, description, duration/views/comments, timestamps, VR/HD flags, maximum resolution, orientation, and preview thumbnail.
2026-08-11d9714c3Released 2.5 with complete typing and py.typed; repaired selectors and fallbacks; implemented get_shorts() on Pornstar and Creator (not the shared base or Channel); and aligned retry defaults with RuntimeConfig.
2026-08-085ce0a28Centralized concurrent scraping on IteratorConfig and forwarded package source/retry defaults.
2026-08-07706fd4fMigrated source-aware media, explicit loading, bounded retries, and structured scrape results to eaf v4.