Python Async v2.4.1

Eporner API

A fully asynchronous Python API wrapper and scraper for Eporner. Fetch video details via API and HTML endpoints, download files directly with multi-threaded range requests, and retrieve pornstar biographies. 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-eporner

For custom CLI printing support, install with the optional cli components:

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

🚀 Quick Start

Run your scraping scripts inside an active event loop using async context:

python
import asyncio
from eporner_api import Client, DownloadConfigRAW
from eporner_api.modules.locals import Encoding

async def main():
    client = Client()

    # Retrieve video info
    video = await client.get_video("https://www.eporner.com/video-12345/example-video")
    print(video.title)
    print(video.views)

    # Download video in best quality using H264 encoding
    config = DownloadConfigRAW(quality="best", path="./downloads")
    await video.download(configuration=config, mode=Encoding.mp4_h264)

asyncio.run(main())

⚙️ Configuration

Adjust timeouts, a proxy or bound interface, request retry/delay settings, cache limits, and iterator concurrency using the shared RuntimeConfig passed into BaseCore.

Please refer to the eaf_base_api Documentation for the complete reference.

python
from base_api import BaseCore
from base_api.modules.config import RuntimeConfig
from eporner_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

All listing methods accept one IteratorConfig | None. If omitted, Eporner eagerly loads both "api" and "html", skips terminal page failures, and installs a handler that skips ResourceGone/NotFound failures (including those nested in media-load errors) while retrying other failures within the resolved budget. A supplied config replaces that complete default.

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=("api", "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. A handler may return RETRY, RAISE, YIELD, or SKIP; retrying remains capped, and a final RETRY falls back to the stage's configured ErrorMode.


🔌 Client

Scraper instance to orchestrate requests and retrieve media objects.

python
from eporner_api import Client
from base_api import BaseCore

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

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. By default, parses the JSON API endpoint first.
await client.get_video( url: str, load_html: bool = False, load_api: bool = True ) -> Video

Parameters

  • url str — The Eporner video URL
  • load_html bool — Parse full HTML elements for detailed fields (default: False)
  • load_api bool — Fetch base properties from Eporner JSON endpoint (default: True)

Returns

→ Video

Search Videos search_videos()

async
Queries search endpoints and streams video results.
async for result in client.search_videos( query: str, sorting_gay: str | Gay, sorting_order: str | Order, sorting_low_quality: str | LowQuality, per_page: int, pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search query words
  • sorting_gay Gay | str — Exclude/include gay content filters (see Sorting Enums)
  • sorting_order Order | str — Sort order (see Sorting Enums)
  • sorting_low_quality LowQuality | str — Low quality exclusions (see Sorting Enums)
  • per_page int — Number of video results per index page
  • pages int — Number of search pages to parse
  • iterator_config IteratorConfig | None — Concurrency, source loading, ordering, retry, and error policy; defaults to Eporner's API+HTML configuration

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Fetch Category Videos get_videos_by_category()

async
Retrieves video lists categorized under specific tags.
async for result in client.get_videos_by_category( category: str | Category, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • category Category | str — Eporner Category enum (e.g. Category._4K)
  • iterator_config IteratorConfig | None — Concurrency, source loading, ordering, retry, and error policy; defaults to Eporner's API+HTML configuration

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Fetch Pornstar get_pornstar()

async
Loads a pornstar profile page containing bio details.
await client.get_pornstar( url: str, load_html: bool = True ) -> Pornstar

Parameters

  • url str — The Eporner pornstar profile URL
  • load_html bool — Pre-load parsed properties (default: True)

Returns

→ Pornstar

Fetch Channel get_channel()

async
Loads a channel profile page containing channel stats, logo, and banner.
await client.get_channel( url: str, load_html: bool = True ) -> Channel

Parameters

  • url str — The Eporner channel profile URL
  • load_html bool — Pre-load parsed properties (default: True)

Returns

→ Channel

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with details extracted from API endpoints and HTML elements. If a video has been deleted or removed by the platform, a ResourceGone error is raised cleanly.

Attributes

AttributeTypeDescription
urlstrThe video page URL
video_idstr | NoneUnique video key ID
keywordslist[str] | NoneList of tags
titlestr | NoneVideo title (loadable from both API and HTML)
viewsint | NoneNumber of views (loadable from both API and HTML)
ratestr | NoneRating representation
publish_datestr | NonePublication / upload date string
length_secondsint | NoneDuration in seconds
length_minutesstr | NoneDuration in minutes (e.g. 12:34)
embed_urlstr | NoneEmbed player path (loadable from both API and HTML)
thumbnailstr | NoneDefault cover image thumbnail URL (loadable from both API and HTML)
rating_valuestr | NoneRating score value
rating_countstr | NoneTotal rating votes
parsed_urlsdict | NoneResolvable CDN paths mapped by resolution (e.g. {"720p": {"h264": "...", "av1": "..."}})
descriptionstr | NoneVideo summary description
encoding_formatstr | NoneEncoding format metadata
is_family_friendlystr | NoneFamily friendly flag
thumbnailslist[str] | NoneList of alternative thumbs (loadable from both API and HTML)
content_urlstr | NoneMeta content URL
best_ratingstr | NoneUpper rating limits
worst_ratingstr | NoneLower rating limits
authors_urlslist[str] | NoneList of actor URLs starring in this video
tagslist[str] | NoneTag labels parsed from the HTML page
categorieslist[str] | NoneCategory labels parsed from the HTML page
uploaderstr | NoneUploader name parsed from the HTML page

Methods

Video Qualities video_qualities()

Returns a list of resolutions available for download.
video.video_qualities() -> list[str]

Returns

→ list[str]

Get URL by Quality get_url_by_quality()

Finds the specific CDN file URL matching the quality height and encoding format.
video.get_url_by_quality( quality: str | int, mode: Encoding | str ) -> str

Parameters

  • quality str | int — Target resolution (e.g. 1080 or "1080p")
  • mode Encoding | str — Video codec format (e.g. Encoding.mp4_h264 or "h264")

Returns

→ str

Download Video download()

async
Downloads the video directly from CDN server using DownloadConfigRAW. Failure raises DownloadFailed with full context and chained cause.
await video.download( configuration: DownloadConfigRAW, mode: Encoding | str, use_workaround: bool = True ) -> bool

Parameters

  • configuration DownloadConfigRAW — RAW download options (quality, path, etc.). See Downloading Options.
  • mode Encoding | str — Video codec format (e.g. Encoding.mp4_h264 or Encoding.av1)
  • use_workaround bool — Enable download pipeline workarounds (default: True)

Returns

→ bool

Get Video Authors get_authors()

async
Yields pornstar profiles starring in this video.
async for star in video.get_authors( load_html: bool = True ) -> AsyncGenerator[Pornstar, None]

Parameters

  • load_html bool — If True, pre-fetches full metadata properties for the yielded pornstar

Returns

→ AsyncGenerator[Pornstar, None]

⭐ Pornstar

dataclass Inherits from BaseProfile → BaseMedia. Represents an Eporner model profile with parsed statistics, bio, and video streams.

Attributes

AttributeTypeDescription
urlstrProfile URL
namestr | NoneName of the pornstar (from BaseProfile)
subscribersstr | NoneNumber of subscribers (from BaseProfile)
picturestr | NoneCover avatar picture URL (from BaseProfile)
video_amountstr | NoneNumber of uploaded/starring videos (from BaseProfile)
video_viewsstr | NoneAccumulated views on videos (from BaseProfile)
websitesdict[str, str] | NoneExternal links and social pages (from BaseProfile)
pornstar_idstr | NoneUnique numeric pornstar ID
photos_amountstr | NoneNumber of photos
pornstar_rankstr | NoneEporner site rank
profile_viewsstr | NoneTotal views of this profile
photo_viewsstr | NoneAccumulated views on photos
countrystr | NoneCountry of origin
agestr | NonePornstar age
ethnicitystr | NoneEthnicity metadata
eye_colorstr | NoneEye color
hair_colorstr | NoneHair color
heightstr | NoneHeight details
weightstr | NoneWeight details
cupstr | NoneBra cup size
measurementsstr | NoneBody measurements string (e.g. 34-24-34)
biographystr | NoneBiography paragraph description
aliaseslist[str] | NoneList of alternate names

Methods

Get Pornstar Videos videos()

async
Yields video scrape results associated with this pornstar. Inherited from BaseProfile.
async for result in pornstar.videos( pages: int = 0, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Pages to load (if 0, automatically calculates pages based on total video_amount)
  • iterator_config IteratorConfig | None — Concurrency, source loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

📺 Channel

dataclass Inherits from BaseProfile → BaseMedia. Represents an Eporner channel profile with rank, stats, logo, banner, and video stream pagination.

Attributes

AttributeTypeDescription
urlstrChannel page URL
namestr | NoneChannel name (from BaseProfile)
subscribersstr | NoneSubscriber count (from BaseProfile)
video_amountstr | NoneTotal uploaded videos count (from BaseProfile)
video_viewsstr | NoneAccumulated video views count (from BaseProfile)
picturestr | NoneProfile/logo picture URL (from BaseProfile)
channel_idstr | NoneUnique numeric channel ID
channel_rankstr | NoneChannel platform rank
logostr | NoneChannel logo image URL
bannerstr | NoneChannel header banner image URL

Methods

Get Channel Videos videos()

async
Yields video scrape results for this channel. Inherited from BaseProfile.
async for result in channel.videos( pages: int = 0, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Pages to load (if 0, automatically calculates pages based on total video_amount)
  • iterator_config IteratorConfig | None — Concurrency, source loading, ordering, retry, and error policy

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

⬇️ Downloading Options

Eporner serves direct MP4 media files. Download options are configured via the DownloadConfigRAW class, specifying the encoding type (AV1 or H.264):

python
from base_api import DownloadConfigRAW
from eporner_api.modules.locals import Encoding

config = DownloadConfigRAW(
    quality="best",            # "best", "half", "worst", or height (e.g. 1080)
    path="./downloads",        # Output directory
    no_title=False,            # Auto-appends title + ".mp4" if False
    allow_multipart=True,      # Enable multi-threaded segmented range downloads
    max_workers=5              # Concurrent segment downloaders
)

# Download using H.264 codec
await video.download(configuration=config, mode=Encoding.mp4_h264)

For more configurations regarding RAW downloaders, please read the reference inside eaf_base_api Documentation.

📊 Scraping Results

Concurrently iterated methods yield immutable ScrapeResult[Video] values. A result contains exactly one of item or error:

python
async for result in client.search_videos("couple", sorting_gay="0", sorting_order="latest", sorting_low_quality="1", per_page=10):
    if result.succeeded:
        video = result.unwrap()     # Returns Video or raises the typed scrape error
        print(video.title)
    else:
        print(f"{result.stage} error for {result.url}: {result.error}")

ScrapeResult Attributes

AttributeTypeDescription
urlstrThe parsed video target URL
stageScrapeStageITEM for media results or PAGE for yielded page failures
page_indexintZero-based target-page index
item_indexint | NoneExtractor position, or None for a page failure
attemptsintNumber of stage attempts used
itemVideo | NoneThe parsed 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

📜 Changelog

2.4.1 (current)

  • 2026-09-15 · 3ee78ee / 6b80518 / f9fd944 / 01429e8 — Updated dependency to eaf-base-api>=4.2.0 and adopted centralized request/download error handling. Failed downloads now raise DownloadFailed with complete context. Added BaseProfile, Pornstar, and Channel classes with profile video scraping via videos(). Added Client.get_channel(). Expanded Video metadata (dual API/HTML source loading for title, views, embed URL, and thumbnails; length_seconds integer type; clean ResourceGone exception when videos are deleted or removed).
  • 2026-08-14 · 7f1bbac — Added HTML-backed tags, categories, and uploader fields; renamed get_available_qualities() to video_qualities(); and made optional pornstar biography fields tolerant of missing page elements.
  • 2026-08-11 · c8f2974 — Added typed ScrapeResult[Video] annotations and py.typed; fixed the Eporner v2 API URL/list response handling and treated package NotFound as a terminal resource error. Default iterator error handling now uses the structured ScrapeErrorContext/ErrorAction handler.
  • 2026-08-08 · f862f1c — Replaced all per-method concurrency, loading, ordering, retry, and callback arguments with IteratorConfig while preserving eager ("api", "html") loading.
  • 2026-08-07 · e3c682a — Migrated to eaf-base-api>=4.0.0: explicit request methods, source-aware media loaders, bounded scheduling, deterministic stream cleanup, structured scrape results, and validated extractors.

📋 Sorting Enums

Search endpoints accept sorting parameters defined inside eporner_api.modules.sorting:

Gay

Enum MemberAPI String ValueFilter Result
Gay.exclude_gay_content"0"Exclude gay search listings
Gay.include_gay_content"1"Include gay search listings
Gay.only_gay_content"2"Only show gay search listings

Order

Enum MemberAPI String ValueDescription
Order.latest"latest"Order by upload date
Order.longest"longest"Order by duration (descending)
Order.shortest"shortest"Order by duration (ascending)
Order.top_rated"top-rated"Order by rating score
Order.most_popular"most-popular"Order by views count
Order.top_weekly"top-weekly"Order by popular weekly trends
Order.top_monthly"top-monthly"Order by popular monthly trends

LowQuality

Enum MemberAPI String ValueFilter Description
LowQuality.exclude_low_quality_content"0"Only include high quality video files (HD)
LowQuality.include_low_quality_content"1"Include both low quality (SD) and high quality (HD) video files
LowQuality.only_low_quality_content"2"Only include low quality video files (SD)

⚠️ Error Handling

Source loaders translate request failures into exceptions from eporner_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 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 eporner_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("Blocked by anti-bot measures.")
    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.

Eporner API includes a command-line interface accessible via eporner_api or python -m eporner_api:

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

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

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