Python · Async · v1.4

Thumbzilla API

A fully asynchronous Python API wrapper and scraper for Thumbzilla. Fetch video metadata, playlists, pornstar biographies, amateur profiles, and studio channels. Stream downloads via HLS with auto-constructed master playlists. Powered by the eaf_base_api networking engine.

$ pip install unofficial-api-for-thumbzilla 📋
GitHub →
⚠️ 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-thumbzilla

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

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

🚀 Quick Start

python
import asyncio
from thumbzilla_api import Client
from base_api import DownloadConfigHLS

async def main():
    client = Client()

    # Fetch video metadata
    video = await client.get_video("https://www.thumbzilla.com/watch/12345")
    print(video.title)
    print(video.views)

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

asyncio.run(main())

⚙️ Configuration

Proxy/interface, timeout, request-attempt/delay, cache, and concurrency settings are handled via RuntimeConfig passed to BaseCore.

Please refer to the eaf_base_api Documentation for details.

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

Main entry point class to execute searches and load resource models.

python
from thumbzilla_api import Client
client = Client()

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.
await client.get_video( url: str, load_html: bool = True ) -> Video

Parameters

  • url str — The Thumbzilla video URL
  • load_html bool — Pre-load parsed properties immediately

Returns

→ Video

Fetch Pornstar get_pornstar()

async
Loads a pornstar profile page and returns a populated Pornstar object.
await client.get_pornstar( url: str, load_html: bool = True ) -> Pornstar

Parameters

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

Returns

→ Pornstar

Fetch Playlist get_playlist()

async
Loads playlist metadata and returns a populated Playlist object.
await client.get_playlist( url: str, load_html: bool = True ) -> Playlist

Parameters

  • url str — The Thumbzilla playlist URL
  • load_html bool — Pre-load parsed properties

Returns

→ Playlist

Fetch Channel get_channel()

async
Loads a production studio channel 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 Amateur get_amateur()

async
Loads an amateur model profile page.
await client.get_amateur( url: str, load_html: bool = True ) -> Amateur

Parameters

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

Returns

→ Amateur

Search Videos search()

async
Queries Thumbzilla search index pages and yields structured video scrape results.
async for result in client.search( query: str, pages: int = 2, iterator_configuration: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • query str — Search query words
  • pages int — Number of search pages to parse
  • iterator_configuration IteratorConfig | None — Concurrency, ordering, source loading, retry, and terminal-error policy. The package default loads the html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with metadata extracted from LD+JSON structured data and JavaScript media definitions.

Attributes

AttributeTypeDescription
urlstrThe video page URL
video_idstr | NoneUnique video key ID
titlestr | NoneVideo title
durationstr | int | NoneVideo duration in seconds
thumbnailstr | NoneThumbnail cover image URL
embed_urlstr | NoneEmbeddable player URL
viewsstr | NoneView count
publish_datestr | NoneUpload date
publish_date_thumbnailstr | NoneThumbnail publication date
descriptionstr | NoneVideo description
author_namestr | NoneName of the uploader
m3u8_urlstr | NoneExternal HLS variants JSON source URL
m3u8_base_urlstr | NoneAuto-constructed HLS master playlist string
media_definitionslist[dict] | NoneRaw media definition configurations
preview_video_urlstr | NonePreview clip path
performerslist[str] | NoneFeatured performer names
uploader_urlstr | NoneUploader profile URL

Methods

Download Video download()

async
Downloads the video via HLS streaming. Appends the video title to the output path unless no_title=True.
await video.download( configuration: DownloadConfigHLS ) -> bool | DownloadReport

Parameters

  • configuration DownloadConfigHLS — HLS download configurations

Returns

→ bool | DownloadReport

📜 Playlist

dataclass Inherits from BaseMedia. Represents a user-curated playlist.

Attributes

AttributeTypeDescription
urlstrThe playlist URL
titlestr | NonePlaylist title
author_namestr | NoneName of the author
rating_percentstr | NoneUser rating percentage
rating_countstr | NoneTotal rating count
viewsstr | NoneNumber of playlist views
videos_countstr | NoneTotal videos count

Methods

Get Playlist Videos get_videos()

async
Yields video scrape results nested inside this playlist.
async for result in playlist.get_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]

👤 User / Pornstar / Amateur

Scraper objects representing creators, actors, and registered users share a common base class (UserHelper).

Attributes

AttributeTypeDescription
urlstrProfile page URL
namestr | NoneUsername / profile display name
pornstar_informationdict | NoneSidebar stat parameters (only populated for Pornstar objects)

Methods

Get Profile Videos get_videos()

async
Yields videos uploaded by or featuring this profile.
async for result in profile.get_videos( pages: int = 2, iterator_configuration: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

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

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

📺 Channel

dataclass Inherits from UserHelper. Represents a publisher studio channel page.

Attributes

AttributeTypeDescription
urlstrChannel page URL
namestr | NoneStudio/channel display name
rankstr | NoneChannel ranking score
viewsstr | NoneTotal channel views count
videos_countstr | NoneTotal uploaded videos count

Methods

Get Channel Videos get_videos()

async
Yields video scrape results uploaded by this studio channel. Inherits from UserHelper.get_videos().
async for result in channel.get_videos( pages: int = 2, iterator_configuration: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

⬇️ Downloading Options

Thumbzilla serves video files via HLS streaming. The scraper auto-constructs a master M3U8 playlist from the JSON media definitions. Configure stream downloading parameters via the DownloadConfigHLS class:

python
from base_api import DownloadConfigHLS

config = DownloadConfigHLS(
    quality="best",
    path="./downloads",
    no_title=False,
    return_report=True
)

report = await video.download(configuration=config)

For more configurations, see the shared eaf_base_api Documentation.

📊 Scraping Results

All concurrently iterated methods accept one IteratorConfig policy and yield immutable ScrapeResult[Video] values. The package default loads html and skips terminal page failures. A supplied configuration replaces that default, so include load_specific_sources=("html",) and page_error_mode=ErrorMode.SKIP when preserving both behaviors; 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 pornstar.get_videos(
    pages=3, iterator_configuration=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. If no retry policy is supplied, it is resolved from RuntimeConfig.request_* values.

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 | NoneThe loaded item on success
errorScrapeOperationError | NoneThe typed terminal error on failure
succeededboolTrue when the result contains an item
unwrap()VideoReturns the item or raises its terminal error

⚠️ Error Handling

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

ExceptionTrigger Cause
NotFoundServer returned HTTP 404 (e.g. video deleted)
NetworkErrorRequest failed due to HTTP connection problems
BotDetectionAnti-bot challenge block detected
ProxyErrorProxy configuration failed or proxy is down
UnknownNetworkErrorUnexpected network errors
DownloadFailedHLS segmented stream download failed
python
from base_api import MediaLoadError
from thumbzilla_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

🖥️ Supported Platforms

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

📝 Changelog

DateCommitChanges
2026-08-110d65160Released 1.4 with complete type hints and the py.typed marker.
2026-08-08b495027Centralized iterator behavior on IteratorConfig, forwarded source/retry defaults, and aligned download-test behavior.
2026-08-08e91bdf3Synchronized the package with the local eaf v4 source and released 1.3.
2026-08-07b2c7678Migrated models, explicit source loading, structured retries, and iterator results to eaf v4.