Python Async v2.5

HQPorner API

A fully asynchronous Python API wrapper and scraper for HQPorner. Fetch videos, metadata, search, get videos by actress or category, and download content — 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-hqporner

For CLI formatting support (recommended), install with the optional cli dependency:

bash
pip install unofficial-api-for-hqporner[cli]
Note
Requires Python ≥ 3.12. Version 2.5 depends on eaf-base-api>=4.0.0, which is installed automatically.

🚀 Quick Start

Every method in this API is asynchronous. You need to run your code inside an async function:

python
import asyncio
from hqporner_api import Client

async def main():
    client = Client()

    # Fetch a video
    video = await client.get_video("https://hqporner.com/hd/...")

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

    # Download the video
    from hqporner_api import DownloadConfigRAW
    config = DownloadConfigRAW(quality="best", path="./downloads")
    await video.download(configuration=config)

asyncio.run(main())

⚙️ Configuration

The entire API relies on eaf_base_api for its networking. Configure one proxy, timeouts, and request attempts through RuntimeConfig, or pass a custom BaseCore into the Client.

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 hqporner_api import Client

my_config = RuntimeConfig()
my_config.proxy = "socks5://127.0.0.1:9050"
my_config.request_attempts = 4  # Total attempts, including the first request

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

Iterator, retry, and error policy

Every listing method now accepts one IteratorConfig | None. HQPorner's default eagerly loads "html", skips terminal page failures, and otherwise resolves concurrency and bounded retries from the core's RuntimeConfig.

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.attempt, context.error)
    return ErrorAction.RETRY

retry = RetryPolicy(
    max_attempts=3, base_delay=0.5, multiplier=2.0, max_delay=8.0, jitter=0.25
)
iterator_config = IteratorConfig(
    max_page_concurrency=2,
    max_item_concurrency=10,
    load_specific_sources=("html",),
    order=ResultOrder.ORIGINAL,
    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,
)

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. Handlers receive ScrapeErrorContext and return ErrorAction.RETRY, RAISE, YIELD, or SKIP; they cannot create an unbounded retry loop.


🔌 Client

The Client class is your entry point for all interactions. It manages the session and headers, providing methods to search, retrieve category lists, and scrape video listings.

Methods

Fetch Video get_video()

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

Parameters

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

Returns

→ Video

Get Videos by Actress get_videos_by_actress()

async
Fetches videos matching the given actress page by page.
async for result in client.get_videos_by_actress( name: str, pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • name str — The actress name (slug format or full URL)
  • pages int — Number of pages to fetch (default: 5)
  • iterator_config IteratorConfig | None — Concurrency, HTML loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Get Videos by Category get_videos_by_category()

async
Fetches videos belonging to a specific category page by page.
async for result in client.get_videos_by_category( category: Category | str, pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • category Category | str — The category name or Enum member
  • pages int — Number of pages to fetch (default: 5)
  • iterator_config IteratorConfig | None — Concurrency, HTML loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Search Videos search_videos()

async
Searches for videos matching the query text. See Search & Filtering for details.
async for result in client.search_videos( query: str, pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search keywords
  • pages int — Number of result pages to iterate
  • iterator_config IteratorConfig | None — Concurrency, HTML loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Get Top Videos get_top_porn()

async
Fetches top rated/viewed videos sorted by a specific interval.
async for result in client.get_top_porn( sort_by: Sort | str, pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • sort_by Sort | str — Sort interval: Sort.ALL_TIME, Sort.WEEK, Sort.MONTH
  • pages int — Number of pages to retrieve
  • iterator_config IteratorConfig | None — Concurrency, HTML loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Get Random Video get_random_video()

async
Queries the random endpoint, extracts three random suggestions, and returns one randomly selected Video object.
await client.get_random_video( load_html: bool = True ) -> Video

Returns

→ Video

Get All Categories get_all_categories()

async
Scrapes and returns a complete list of all category slug strings available on HQPorner.
await client.get_all_categories() -> list[str]

Returns

→ list[str]

Get Brazzers Videos get_brazzers_videos()

async
Fetches free Brazzers videos hosted on HQPorner.
async for result in client.get_brazzers_videos( pages: int = 5, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Number of pages to fetch
  • iterator_config IteratorConfig | None — Concurrency, HTML loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with extracted streaming qualities and download links.

Attributes

AttributeTypeDescription
urlstrThe video page URL
titlestr | NoneVideo title
cdn_urlstr | NoneThe Altplayer CDN iframe/block URL path
pornstarslist[str] | NoneList of actress names featured in the video
lengthstr | NoneVideo length/duration (e.g., 15:45)
publish_datestr | NoneUpload date string
tagslist[str] | NoneList of category/tag strings
direct_download_urlslist[str] | NoneResolved direct MP4 links from Altplayer
thumbnailstr | NoneVideo preview/thumbnail URL

Methods & Properties

Available Qualities video_qualities

property
Returns a sorted list of available qualities (resolutions like ["360", "480", "720", "1080"]).
video.video_qualities -> list[str]

Returns

→ list[str]

Download Video download()

async
Downloads the video direct MP4 files using range/multipart support. Appends the video title to path unless no_title=True. Ordinary failures raise DownloadFailed with full diagnostic context and chained cause.
await video.download( configuration: DownloadConfigRAW ) -> bool
  • configuration DownloadConfigRAW — RAW download settings. See Downloading.

Returns

→ bool

🔍 Search & Filtering

Perform searches or sort categories using these helper Enums:

python
from base_api import ResultOrder
from base_api.modules.config import IteratorConfig
from hqporner_api import Category, Sort

async for result in client.get_videos_by_category(
    category=Category.QUALITY_4K,
    pages=3,
    iterator_config=IteratorConfig(
        load_specific_sources=("html",),
        order=ResultOrder.ORIGINAL,
    ),
):
    if result.succeeded:
        print(result.unwrap().title)

Sorting Enums

Sort (Top Lists)

Enum ValueDescription
Sort.ALL_TIMEAll time top videos
Sort.WEEKWeekly top videos
Sort.MONTHMonthly top videos

Category (Slugs)

A partial list of available categories within the Category StrEnum (refer to hqporner_api.modules.locals for the full list):

Enum MemberSlug Value
Category.QUALITY_HD"1080p-porn"
Category.QUALITY_4K"4k-porn"
Category.FPS_60"60fps-porn"
Category.MILF"milf"
Category.LESBIAN"lesbian"
Category.POV"pov"
Category.ASIAN"asian"
Category.AMATEUR"amateur"

⬇️ Downloading

HQPorner videos host direct MP4 media files instead of HLS streams. All downloads are configured using the DownloadConfigRAW dataclass:

python
from hqporner_api import DownloadConfigRAW

config = DownloadConfigRAW(
    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
    allow_multipart=True,     # Use multi-threaded range requests
    max_workers=5             # Number of parallel download threads
)

success = await video.download(configuration=config)

DownloadConfigRAW — Full Options

ParameterTypeDefaultDescription
qualitystr | int—Required. "best", "half", "worst", or resolution height (360, 480, 720, 1080, etc.)
pathstr"./"Output directory or exact file path
no_titleboolFalseIf True, treat path as the exact destination filename
callback(int, int) → NoneNoneProgress callback (downloaded, total)
stop_eventasyncio.EventNoneSet this event to cancel the download
allow_multipartboolTrueEnable parallel segmented downloads using range requests
max_workersint5Maximum concurrent worker threads for downloading segments
read_timeoutfloat120.0Connection read timeout in seconds for chunk fetching
chunk_sizeint1024Download buffer chunk size in bytes
max_retriesint5Max retries per worker when downloading chunks

📄 Pagination & Iterators

Methods like search_videos(), get_videos_by_actress(), and get_videos_by_category() return async generators yielding immutable ScrapeResult[Video] instances:

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

ScrapeResult

AttributeTypeDescription
urlstrThe video URL
stageScrapeStageITEM for media results or PAGE for yielded page failures
page_indexintZero-based page index
item_indexint | NoneExtractor position, or None for a page failure
attemptsintNumber of stage attempts used
itemVideo | NoneThe populated Video when successful
errorScrapeOperationError | NoneThe typed page or item failure
succeededboolTrue when item is present
unwrap()VideoReturns the item or raises the stored typed error

IteratorConfig Parameters

All iterator methods accept a single iterator_config. The most commonly customized fields are:

  • max_item_concurrency int | None — Concurrent Video work; None resolves from RuntimeConfig.videos_concurrency.
  • max_page_concurrency int | None — Concurrent page work; None resolves from RuntimeConfig.pages_concurrency.
  • load_specific_sources Iterable[str] — Keep ("html",) to preserve HQPorner's populated Video results.
  • order ResultOrder | str — Completion order by default; use ResultOrder.ORIGINAL for page/extractor order.
  • page_retry / item_retry RetryPolicy | None — Bounded stage policies; None resolves from RuntimeConfig.
  • page_error_handler / item_error_handler ErrorHandler | None — Structured handlers receiving ScrapeErrorContext and returning ErrorAction.

⚠️ Error Handling

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

InvalidActress derives from ScraperException. NotAvailable derives from the shared VideoUnavailable and is still raised directly when no download qualities are available. Both retain their default messages and accept a custom message.

ExceptionWhen Raised
InvalidActressThe actress name or URL pattern did not match checks
NotAvailableNo download qualities are available; this error subclasses VideoUnavailable
NotFoundServer returned HTTP 404
NetworkErrorRequest failed due to a network error, exhausted request retries, or a non-404 HTTPStatusError
BotDetectionBot protection triggered
ProxyErrorInvalid or failing proxy
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 hqporner_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("Bypassing bot protection failed")
    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 HQPorner API package includes a rich CLI accessible via hqporner_api or python -m hqporner_api:

bash
# Download a single video
hqporner_api --download "https://hqporner.com/hd/..." --quality best --output ./downloads --no-title False

# Search and download resulting videos
hqporner_api --search "blonde" --pages 2 --quality 1080 --output ./blonde_videos/ --concurrency 3

CLI Options

FlagDescription
--download URLDownload a single video from the specified URL
--file FILERead and download URLs from a line-separated text file
--search QUERYSearch videos by keyword and download matches
--actress NAMEDownload videos featuring the actress name or URL
--category CATEGORYDownload videos from a specific category
--randomDownload a single randomly chosen video
--quality QUALITYRequired. The video quality: best, half, worst, or specific height (e.g. 720)
--output DIRRequired. The destination path/directory
--no-title True/FalseSkip auto-appending video title to output filename (default: False)
--pages NNumber of pages to scrape (default: 1)
--concurrency NMax concurrent downloads to run in parallel (default: 3)

📜 Changelog

2.5 (current)

  • 2026-09-15 · fccc573 / 4e702e9 — 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 diagnostic context. Added layout anchor validations to detect page structure changes. Implemented robust fallback selectors for video metadata and CDN URLs. Enhanced download quality resolution using PATTERN_RESOLUTION and validation. Added snippet-based unit tests for HTML extraction and layout checks.
  • 2026-08-11 · a097c19 — Released 2.5, added generic ScrapeResult[Video] annotations and the py.typed marker, and switched missing iterator policies to live RuntimeConfig-derived defaults.
  • 2026-08-08 · e3055d4 — Centralized every listing method on IteratorConfig, preserving required ("html",) loading and page-failure skipping while removing legacy concurrency/loading/order/callback parameters.
  • 2026-08-07 · eaceacf — Migrated to eaf-base-api>=4.0.0: explicit request methods, source-aware fields/loaders, bounded scheduling, deterministic stream cleanup, structured results/errors, and extractor validation.

🖥️ Supported Platforms

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