A Python client library for Fab.com marketplace API, extending asset-marketplace-client-core for multi-platform compatibility.
Version 2.0.0 - Now extends asset-marketplace-client-core. See Migration Guide for upgrading from v1.x.
- 🔌 Pluggable Auth - Bring your own authentication provider
- 🌐 Configurable Endpoints - Runtime endpoint configuration
- 📦 Custom Parsers - Implement parsers for any content format
- ✅ Type Safe - Full type hints and dataclasses with mypy strict mode
- 🔍 Validation - Optional JSON schema validation
- 🎯 Clean API - Callback-based, no side effects, proper exception handling
- 🔒 Security First - Path traversal prevention, URL validation, secure defaults
- 🚀 Modern Tooling - uv + hatchling + pyproject.toml
- 🤝 Multi-Platform - Extends core library for compatibility across marketplaces
- Python 3.9 or higher
- Dependencies:
asset-marketplace-client-core>=0.1.0,requests>=2.28.0
# Using uv (recommended)
uv pip install fab-api-client
# Using pip
pip install fab-api-client
# For development
cd fab-api-client
uv sync --extra dev
# Optional extras
uv pip install fab-api-client[validation] # JSON schema validation
uv pip install fab-api-client[cli] # CLI tools (tqdm)from fab_api_client import FabClient, CookieAuthProvider, FabEndpoints
# Configure API endpoints
endpoints = FabEndpoints(
base_url="https://example.com",
library_search="https://example.com/api/library/search",
asset_formats="https://example.com/api/assets/{asset_uid}/formats",
download_info="https://example.com/api/assets/{asset_uid}/files/{file_uid}/download"
)
# Create authentication provider
auth = CookieAuthProvider(
cookies={"session_id": "xxx", "csrf_token": "yyy"},
endpoints=endpoints
)
# Create client
client = FabClient(auth=auth)
# Fetch library
library = client.get_library()
print(f"Found {library.total_count} assets")
# Download manifests
for asset in library.assets[:5]:
result = client.download_manifest(asset, output_dir="./manifests")
if result.success:
manifest = result.load()
print(f"✅ {asset.title}: {len(manifest.files)} files")Implement FabAuthProvider for your service:
from fab_api_client import FabAuthProvider, ApiEndpoints
import requests
class MyAuthProvider(FabAuthProvider):
def __init__(self, api_key: str):
self.api_key = api_key
self.session = requests.Session()
self.session.headers["Authorization"] = f"Bearer {api_key}"
def get_session(self) -> requests.Session:
return self.session
def get_endpoints(self) -> ApiEndpoints:
return ApiEndpoints(
library_search="https://myapi.com/library",
asset_formats="https://myapi.com/assets/{asset_uid}/formats",
download_info="https://myapi.com/assets/{asset_uid}/download/{file_uid}"
)
# Use custom provider
auth = MyAuthProvider(api_key="your-key")
client = FabClient(auth=auth)Implement ManifestParser for your format:
from fab_api_client import ManifestParser, ParsedManifest
import json
class CustomParser(ManifestParser):
def parse(self, data: bytes) -> ParsedManifest:
# Parse your custom format
manifest_data = json.loads(data)
return ParsedManifest.from_dict(manifest_data)
# Use custom parser
parser = CustomParser()
client = FabClient(auth=auth, manifest_parser=parser)# Manual pagination for large libraries
for page in client.get_library_pages():
assets = page.to_assets()
print(f"Page: {len(assets)} assets")
# Process page...def on_progress(asset, status):
print(f"[{asset.title}] {status}")
results = client.download_manifests(
library.assets,
output_dir="./manifests",
on_progress=on_progress
)with FabClient(auth=auth) as client:
library = client.get_library()
# Session automatically closedlibrary = client.get_library()
# Filter by predicate
matching = library.filter(lambda a: "keyword" in a.title)
# Find specific asset
asset = library.find_by_uid("asset-uid-here")The library uses a clean two-layer type system with models organized into separate files:
Domain Models (fab_api_client.models.domain):
- Business entities:
Asset,Library,Listing,License,Seller - Format details:
AssetFormat,AssetFormatType,TechnicalSpecs - Manifest types:
ParsedManifest,ManifestFile,DownloadResult - Capabilities:
Capabilities
API Response Types (fab_api_client.models.api):
LibrarySearchResponse- Paginated library search resultsAssetFormatsResponse- Asset format informationDownloadInfoResponse- Download URLs and metadataCursorInfo- Pagination cursor data
All models are exported from fab_api_client.models for convenient imports.
Main API client.
FabClient(
auth: FabAuthProvider,
manifest_parser: Optional[ManifestParser] = None,
verify_ssl: bool = True,
timeout: int = 30,
)Methods:
get_library(sort_by: str = '-createdAt') -> Library- Fetch complete libraryget_library_pages(sort_by: str = '-createdAt') -> Iterator[LibrarySearchResponse]- Paginated accessdownload_manifest(asset: Asset, output_dir: Path, on_progress: Optional[Callable] = None) -> DownloadResultdownload_manifests(assets: List[Asset], output_dir: Path, on_progress: Optional[Callable] = None) -> List[DownloadResult]
Collection of assets with helper methods.
assets: List[Asset]- All assetstotal_count: int- Total countfilter(predicate) -> Library- Filter assets by predicate functionfind_by_uid(uid: str) -> Optional[Asset]- Find asset by UID
Represents an asset/entitlement in your library.
uid: str- Unique identifiertitle: str- Asset namecreated_at: Optional[datetime]- When added to librarystatus: str- Entitlement status (e.g., "approved")capabilities: Optional[Capabilities]- Entitlement capabilitiesgranted_licenses: List[License]- Licenses granted with this entitlementlisting: Optional[Listing]- Marketplace listing informationraw_data: Dict[str, Any]- Complete raw API data for extensibility
Marketplace listing details for an asset.
title: str- Listing titleuid: str- Unique identifierlisting_type: str- Type of listingdescription: Optional[str]- Descriptiontags: List[str]- Tagsis_mature: bool- Mature content flaglast_updated_at: Optional[datetime]- Last update timestamplicenses: List[License]- Available licensesseller: Optional[Seller]- Seller informationasset_formats: List[AssetFormat]- Available formatsraw_data: Dict[str, Any]- Complete raw data
License information for an asset.
name: str- License nameslug: str- License slugurl: Optional[str]- License URLtype: Optional[str]- License typeis_cc0: bool- Whether it's CC0 licensedprice_tier: Optional[str]- Price tieruid: Optional[str]- Unique identifier
Seller/creator information.
seller_id: str- Seller IDseller_name: str- Seller nameuid: str- Unique identifierprofile_image_url: Optional[str]- Profile image URLcover_image_url: Optional[str]- Cover image URLis_seller: bool- Whether user is a seller
Asset format details including technical specifications.
asset_format_type: AssetFormatType- Format type informationtechnical_specs: Optional[TechnicalSpecs]- Technical specificationsversions: List[Dict[str, Any]]- Version informationraw_data: Dict[str, Any]- Complete raw data
Asset format type information.
code: str- Format code (e.g., "unreal-engine")name: str- Format nameicon: str- Icon identifiergroup_name: str- Format group nameextensions: List[str]- File extensions
Technical specifications for an asset format.
unreal_engine_engine_versions: List[str]- Supported UE versionsunreal_engine_target_platforms: List[str]- Target platformsunreal_engine_distribution_method: str- Distribution methodtechnical_details: Optional[str]- Additional technical details
Entitlement capabilities.
add_by_verse: bool- Can add by Verserequest_download_url: bool- Can request download URL
Parsed manifest data.
version: str- Manifest file versionapp_id: str- Application IDapp_name: str- Application namebuild_version: str- Build version stringfiles: List[ManifestFile]- File listraw_data: Dict[str, Any]- Complete raw manifest datafrom_dict(data: Dict) -> ParsedManifest- Create from dictionary
Individual file entry in manifest.
filename: str- File namefile_hash: str- File hashfile_chunk_parts: List[Dict[str, Any]]- Chunk parts information
Result of manifest download operation.
success: bool- Whether successfulfile_path: Optional[Path]- Saved file pathsize: Optional[int]- File size in byteserror: Optional[str]- Error message if failedload() -> ParsedManifest- Load and parse manifest file
Abstract base for authentication providers.
get_session() -> requests.Session- Return configured sessionget_endpoints() -> ApiEndpoints- Return endpoint configuration
Generic cookie-based authentication.
__init__(cookies: Dict[str, str], endpoints: ApiEndpoints)
Endpoint URL configuration.
library_search: str- Library search endpointasset_formats: str- Asset formats endpoint (template with{asset_uid})download_info: str- Download info endpoint (template with{asset_uid}and{file_uid})
Abstract base for manifest parsers.
parse(data: bytes) -> ParsedManifest- Parse manifest bytes
Default JSON manifest parser.
FabError- Base exceptionFabAuthenticationError- Authentication failed (401, 403)FabAPIError- HTTP errors from APIFabNotFoundError- Resource not found (404)FabNetworkError- Network/timeout errors
sanitize_filename(title: str) -> str
validate_manifest(manifest_path: Path, schema_path: Optional[Path] = None) -> bool
detect_manifest_format(manifest_path: Path) -> str # "json" or "binary"Requires optional jsonschema package:
pip install jsonschemafrom fab_api_client import validate_manifest
from pathlib import Path
result = client.download_manifest(asset, "./manifests")
manifest = result.load()
if isinstance(manifest, ParsedManifest):
is_valid = validate_manifest(result.file_path)
if is_valid:
print("✅ Manifest schema is valid")# Clone and setup
git clone https://github.com/brentlopez/fab-api-client
cd fab-api-client
uv sync --extra dev
# Run tests
uv run pytest
# Type checking
uv run mypy src/
# Linting and formatting
uv run ruff format src/
uv run ruff check src/
# Security audit
uv run pip-auditPython Version:
- Minimum version increased from 3.7 to 3.9
Type Names:
ApiEndpoints→FabEndpoints(alias maintained for compatibility)DownloadResult→ManifestDownloadResultfor manifest operations (alias maintained)
Endpoint Configuration:
# v1.x
endpoints = ApiEndpoints(
library_search="https://...",
asset_formats="https://...",
download_info="https://..."
)
# v2.x (requires base_url)
endpoints = FabEndpoints(
base_url="https://...", # NEW: Required field
library_search="https://...",
asset_formats="https://...",
download_info="https://..."
)Manifest Downloads:
# v1.x
manifest = client.download_manifest(asset, "./manifests")
print(f"Version: {manifest.version}")
# v2.x
result = client.download_manifest(asset, "./manifests")
if result.success:
manifest = result.load() # Parse the manifest
print(f"Version: {manifest.version}")New Methods:
client.get_asset(asset_uid)- Fetch single asset by UIDclient.download_asset(asset_uid, output_dir, ...)- Download using core interfaceclient.close()- Explicit resource cleanup
Security Improvements:
- Path traversal prevention
- URL validation before downloads
- Secure filename sanitization
- Configurable rate limiting
Multi-Platform Compatibility:
Now extends asset-marketplace-client-core for compatibility with other marketplace clients.
For detailed migration instructions, see CHANGELOG.md.
MIT License - see LICENSE file for details.