Media Downloads Guide¶
Download images, videos, and thumbnails from ad creatives to your local filesystem.
Quick start¶
from meta_ads_collector import MetaAdsCollector
with MetaAdsCollector() as collector:
for ad, media_results in collector.collect_with_media(
query="fashion",
country="US",
max_results=20,
media_output_dir="./fashion_media",
):
for result in media_results:
if result.success:
print(f" {result.media_type}: {result.local_path}")
How it works¶
For each ad creative, the downloader attempts to download every available media URL:
| Media Type | Source Field | Description |
|---|---|---|
image |
creative.image_url |
Main creative image |
video_hd |
creative.video_hd_url |
HD video |
video_sd |
creative.video_sd_url |
SD video |
thumbnail |
creative.thumbnail_url |
Video preview/thumbnail image |
Files are saved as {ad_id}_{creative_index}_{media_type}.{ext}, for example:
- 123456_0_image.jpg
- 123456_0_video_hd.mp4
- 123456_1_thumbnail.webp
File extension detection¶
Extensions are resolved in priority order:
- Recognizable extension in the URL path (e.g.,
.jpg,.mp4) Content-Typeheader from the HTTP response- Fallback:
.bin
Collect with media¶
The collect_with_media() method on MetaAdsCollector yields (Ad, list[MediaDownloadResult]) tuples:
from meta_ads_collector import MetaAdsCollector
with MetaAdsCollector() as collector:
for ad, results in collector.collect_with_media(
query="tech",
media_output_dir="./media",
max_results=50,
):
successful = [r for r in results if r.success]
failed = [r for r in results if not r.success]
print(f"Ad {ad.id}: {len(successful)} downloaded, {len(failed)} failed")
The ad is yielded even when one or more media downloads fail. Each attempted download has a result entry with success=False and an error message; an empty result list means no downloadable media URL was available or the download loop itself could not produce a result.
Download media for a single ad¶
from meta_ads_collector import MetaAdsCollector
with MetaAdsCollector() as collector:
ad = next(collector.search(query="test", max_results=1), None)
if ad is None:
print("No ads found")
else:
results = collector.download_ad_media(ad, output_dir="./single_ad_media")
for result in results:
print(f"{result.media_type}: success={result.success}, path={result.local_path}")
Using MediaDownloader directly¶
This standalone example retrieves an ad first, then passes it to MediaDownloader:
from meta_ads_collector import MediaDownloader, MetaAdsCollector
with MetaAdsCollector() as collector:
ad = next(collector.search(query="test", max_results=1), None)
if ad is not None:
downloader = MediaDownloader(
output_dir="./media",
timeout=30,
max_retries=2,
)
results = downloader.download_ad_media(ad)
print(results)
else:
print("No ads found")
MediaDownloadResult¶
Each download attempt produces a MediaDownloadResult:
| Field | Type | Description |
|---|---|---|
ad_id |
str |
The ad archive ID |
creative_index |
int |
Zero-based index of the creative |
media_type |
str |
image, video_hd, video_sd, or thumbnail |
url |
str |
The source URL |
local_path |
str or None |
Absolute path to the downloaded file |
success |
bool |
Whether the download succeeded |
error |
str or None |
Error message on failure |
file_size |
int or None |
File size in bytes on success |
CLI usage¶
# Download media alongside ad collection
meta-ads-collector -q "fashion" --download-media --media-dir ./fashion_media -o ads.json
# Custom media directory
meta-ads-collector -q "tech" --download-media --media-dir /data/ad_media -o tech.json
Retry behavior¶
Downloads retry up to max_retries times (default 2) with exponential backoff. The downloader treats HTTP 403 as a likely expired URL and does not retry it; a 403 response alone does not establish why access was denied.
Completed files with non-zero size are skipped automatically. Downloads use temporary files and are renamed only after success; interrupted .part files are never treated as completed downloads. Returned paths are absolute, and filenames are confined to the output directory.