Python · Async · v1.4

Redtube API

A fully asynchronous Python API wrapper and scraper for Redtube. Fetch video details, custom playlists, publishers channels, users, and pornstars. Stream downloads via HLS. Powered by the eaf_base_api networking engine.

$ pip install unofficial-api-for-redtube 📋
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-redtube

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

bash
pip install unofficial-api-for-redtube[av]
Note
Requires Python ≥ 3.12. eaf_base_api ≥ 4.0.0 is installed automatically.

🚀 Quick Start

Every scraper operation is asynchronous. Wrap calls in a running event loop:

python
import asyncio
from redtube_api import Client
from base_api import DownloadConfigHLS

async def main():
    client = Client()

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

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

asyncio.run(main())

⚙️ Configuration

Redtube API uses eaf_base_api ≥ 4.0.0. Its singular proxy, request attempts, timeouts, and other networking behavior are configured through 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 redtube_api import Client

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

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

🔌 Client

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

python
from redtube_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 a video profile page and returns a populated Video object. Parses the configuration script embedded inside the HTML.
await client.get_video( url: str, load_html: bool = True ) -> Video

Parameters

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

Returns

→ Video

Fetch Pornstar get_pornstar()

async
Loads a model 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 information metadata and returns an Playlist object.
await client.get_playlist( url: str, load_html: bool = True ) -> Playlist

Parameters

  • url str — The Redtube 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

Fetch User get_user()

async
Loads a registered user account profile page.
await client.get_user( url: str, load_html: bool = True ) -> User

Parameters

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

Returns

→ User

Search Videos search()

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

Parameters

  • query str — Search query words
  • pages int — Number of search pages to parse
  • iterator_config IteratorConfig | None — Optional v4 concurrency, ordering, eager-source, retry, and error-handling policy. The package default eagerly loads html.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

🎬 Video

dataclass Inherits from BaseMedia. Represents a single video with details extracted from JavaScript parameters embedded in the page.

Attributes

AttributeTypeDescription
urlstrThe video page URL
video_idstr | NoneUnique video key ID
titlestr | NoneVideo title
durationint | str | NoneVideo duration supplied by the page player data
thumbnailstr | NoneThumbnail cover image URL
embed_codestr | NoneEmbed code for web players
localestr | NoneLocale code identifier
media_definitionslist[dict] | NoneRaw media definitions from the player configuration
is_auto_play_enabledbool | NoneAutoplay status
is_vrbool | NoneVR video flag
author_urlstr | NoneThe author's profile page URL
m3u8_source_urlstr | NoneThe raw HLS playlists URL source path
mp4_urlstr | NoneRaw MP4 direct download link (if available)
action_tags_rawobjectRaw action-tag payload (currently a string or mapping at runtime)
action_tagsdict | NoneDecoded action tags mapped by keyword and timestamp
m3u8_base_urlstr | NoneConstructed HLS master playlist string
author_namestr | NoneName of the video author
uploader_idstr | NoneUploader identifier
uploader_typestr | NoneUploader type
preview_video_urlstr | NonePreview clip path
pornstars_nameslist[str] | NoneNames of starring pornstars
pornstars_urlslist[str] | Nonestarring pornstar profile links

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.
await video.download( configuration: DownloadConfigHLS ) -> bool | DownloadReport

Parameters

  • configuration DownloadConfigHLS — HLS download configurations

Returns

→ bool | DownloadReport

Fetch Video Author author()

async
Fetches the uploader profile, automatically returning either an Amateur, Pornstar, or Channel object.
await video.author( load_html: bool = False ) -> Amateur | Pornstar | Channel

Parameters

  • load_html bool — If True, pre-fetches full author metadata immediately

Returns

→ Amateur | Pornstar | Channel

📜 Playlist

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

Attributes

AttributeTypeDescription
urlstrThe playlist URL
titlestr | NonePlaylist title
author_urlstr | NoneAuthor's relative profile path
author_namestr | NoneName of the author
rating_percentstr | NoneUser rating percentage
rating_countstr | NoneTotal rating count
viewsstr | NoneNumber of playlist views
video_countstr | NoneTotal videos count
updated_atstr | NoneLast updated timestamp string
statusstr | NoneStatus description

Methods

Fetch Playlist Author get_author()

async
Returns a populated User object representing the owner of this playlist.
await playlist.get_author( load_html: bool = False ) -> User

Parameters

  • load_html bool — If True, pre-fetches full user metadata immediately

Returns

→ User

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 — Optional v4 iterator policy. The default eagerly loads each video's html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

👤 User / Pornstar / Amateur

Scraper objects representing creators, actors, and registered users share metadata hierarchies (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_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Video], None]

Parameters

  • pages int — Pages to load
  • iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

Get User Playlists get_playlists()

async
Yields playlists created by the user. Available for User objects only.
async for result in user.get_playlists( pages: int = 2, iterator_config: IteratorConfig | None = None ) -> AsyncGenerator[ScrapeResult[Playlist], None]

Parameters

  • pages int — Pages to load
  • iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each playlist's html source.

Returns

→ AsyncGenerator[ScrapeResult[Playlist], None]

📺 Channel

dataclass Inherits from BaseMedia. 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
subscribers_countstr | NoneTotal channel subscribers count

Methods

Get Channel Videos get_videos()

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

Parameters

  • pages int — Pages to load
  • iterator_config IteratorConfig | None — Optional v4 iterator policy. The default eagerly loads each video's html source.

Returns

→ AsyncGenerator[ScrapeResult[Video], None]

⬇️ Downloading Options

Redtube serves video files via HLS streaming. Configure stream downloading parameters via the DownloadConfigHLS class:

python
from base_api import DownloadConfigHLS

config = DownloadConfigHLS(
    quality="best",            # "best", "half", "worst", or height int (e.g. 720)
    path="./downloads",        # Destination path
    no_title=False,            # If False, automatically appends title + ".mp4"
)

success = await video.download(configuration=config)

For more configurations regarding downloading setups, please refer to the shared eaf_base_api Documentation.

📊 Scraping Results

Iterator methods yield a typed ScrapeResult[T] wrapper. Use succeeded to branch safely or unwrap() to return the item and raise its terminal error on failure:

python
async for result in client.search("beach", pages=2):
    if result.succeeded:
        video = result.unwrap()
        print(video.title)
    else:
        print(result.stage, result.url, result.error)

ScrapeResult Attributes

AttributeTypeDescription
stageScrapeStageWhether the result came from the page or item stage
urlstrThe parsed video target URL
page_indexintZero-based source page index
item_indexint | NoneZero-based item index, or None for a page failure
attemptsintNumber of attempts used by the yielding stage
itemT | NoneThe parsed Video or Playlist on success
errorScrapeOperationError | NoneThe typed terminal page or item error on failure
succeededboolTrue when error is None

IteratorConfig, bounded retries, and custom handlers

All iterator-only controls now live in one IteratorConfig. Redtube's package default uses ErrorMode.SKIP for terminal page failures; a supplied config replaces that behavior, and a bare IteratorConfig instead defaults to ErrorMode.YIELD. A retry policy's max_attempts includes the initial attempt, so this example makes at most three stage attempts per failed page or item. Each stage attempt may itself perform the request retries configured on BaseCore. Fully populated results require the html source.

Leave page_retry or item_retry as None to derive that stage's bounded policy from the active RuntimeConfig request-attempt and backoff settings; an explicit RetryPolicy overrides it per stage.

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

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

retry = RetryPolicy(
    max_attempts=3, base_delay=0.5, multiplier=2.0,
    max_delay=4.0, jitter=0.2
)
iterator_config = IteratorConfig(
    max_page_concurrency=2,
    max_item_concurrency=8,
    max_pending_items=16,
    load_specific_sources=("html",),
    page_retry=retry,
    item_retry=retry,
    page_error_handler=handle_scrape_error,
    item_error_handler=handle_scrape_error,
)

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

ScrapeErrorContext supplies stage, url, error, attempt, max_attempts, page_index, and item_index. A handler returns ErrorAction.RETRY, RAISE, YIELD, or SKIP.

Independent page and item handlers

The core routes page-stage failures to page_error_handler and item-stage failures to item_error_handler. Assign the same callable to both fields only when both stages should use the same policy; otherwise configure either handler independently.

⚠️ Error Handling

Source loaders translate request failures into exceptions from redtube_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
BotDetectionCloudflare 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 redtube_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.4 — 2026-08-11

  • 8fcf5ec — Added generic ScrapeResult typing and the PEP 561 py.typed marker, left iterator retry policies unset so they resolve from RuntimeConfig, and released 1.4.

1.3 — 2026-08-08

  • eb45470 — Consolidated playlist, user, channel, and search iterator concurrency, ordering, source loading, retry, and error controls into IteratorConfig.

1.3 migration — 2026-08-07

  • 46fc0fd — Migrated to the eaf_base_api v4 request and scraping model: fetch_text()/request(), structured exceptions, source-aware media loading, typed scrape streams/results, and bounded retries.

🖥️ Supported Platforms

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