To get an Amazon product's video URLs, call the SellerMagnet /api/amazon-product-lookup endpoint and read productInfo.videos: a list of at most two HLS playlist URLs ending in .m3u8, never a direct .mp4 link. The field is null or an empty list when the listing has no gallery video - not an error. Because the URLs are HLS manifests, a plain file download only saves a short text playlist; play it with an HLS-aware tool or convert it with ffmpeg.
Key Takeaways
- productInfo.videos returns up to two HLS playlist URLs (.m3u8), capped by the extraction step that builds the field - more videos can exist on the page and still return only two.
- A video URL is a manifest, not a file: a plain GET downloads a text playlist, not the video.
- Null or an empty list means "no gallery video", the same case, never a retryable error.
- ffmpeg joins the HLS segments into one MP4 without re-encoding: -c copy.
- Reading videos costs no extra credit - it comes back on the same product lookup as every other field.
What does productInfo.videos actually return?
The lookup response carries productInfo.videos as a plain list of strings. Each string is the URL of an HLS playlist (.m3u8): a short text file that lists the video's segments and quality variants, not the video itself. The list holds at most two entries because the extraction step behind this field caps itself at two gallery videos per listing, so a product page with three or four videos still reports only the first two. An empty list or a missing field both mean the listing has no gallery video. The list holds URLs only: no title, duration or thumbnail per video, so read those from the player if you need them.
| Field | Type | What you get |
|---|---|---|
productInfo.images | list[str] | every gallery image, highest resolution offered |
productInfo.mainImage | str | one offer-listing image, often smaller than the gallery |
productInfo.videos | list[str] | up to two HLS playlist URLs (.m3u8) |
productInfo.hasAPlusContent | bool | true for A+, a brand story or product documents |
Media fields in a lookup response (trimmed, illustrative values)
{
"success": true,
"data": {
"productInfo": {
"asin": "B0CL61F39H",
"videos": [
"https://m.media-amazon.com/S/vse-vms-transcoding-artifact-us-east-1-prod/8af0ddf1-55f5-4e71-9463-39602c3edbae/default.jobtemplate.hls.m3u8",
"https://m.media-amazon.com/S/vse-vms-transcoding-artifact-us-east-1-prod/3b2e17a2-9e4d-4f31-9c6b-0b7a1f9d6a21/default.jobtemplate.hls.m3u8"
],
"hasAPlusContent": true
}
}
}

Why can't I just download videos[0] as an MP4?
An .m3u8 URL is an HLS (HTTP Live Streaming) playlist: a text file that lists the video's segments, each a few seconds of a .ts or fragmented .mp4 chunk, sometimes in several quality variants. A plain requests.get(url) only downloads that text file - it is not the video and will not play in a media player expecting a single .mp4. You need either a player that understands HLS (a browser with hls.js, or Safari, which plays .m3u8 natively) or a tool that follows the manifest and joins the segments itself, such as ffmpeg.
The sample below reuses the two helper files from the Python quickstart: sellermagnet.py provides ApiError and new_session, and retry.py provides get_with_retry, which retries 429, 502, 503, a single 500 and dropped connections, and raises every other error at once.
video_urls.py - detect how many HLS videos a listing has
"""Read productInfo.videos: at most two HLS playlists (.m3u8) per listing, never an MP4 link.
The API's extraction step keeps at most two gallery videos per listing, so a product page with more
still returns only the first two. A null or empty field means "no gallery video", not an error:
don't retry it and don't count it as a failed call. Sample ASINs other than B0CL61F39H are illustrative.
"""
from retry import get_with_retry
from sellermagnet import ApiError, new_session
MARKETPLACE = "ATVPDKIKX0DER" # amazon.com
def is_hls_playlist(url: str) -> bool:
"""Every URL in videos is an HLS manifest, never a direct .mp4: check before you try to
save it as one file with a plain HTTP GET."""
return url.rsplit("?", 1)[0].endswith(".m3u8")
def video_report(session, asin: str, marketplace_id: str = MARKETPLACE) -> dict:
p = get_with_retry(session, "amazon-product-lookup", asin=asin, marketplaceId=marketplace_id)["productInfo"]
urls = p.get("videos") or [] # null, [] and a missing key all mean "no gallery video", not an error
non_hls = [u for u in urls if not is_hls_playlist(u)]
return {
"asin": p["asin"],
"video_count": len(urls),
"urls": urls,
"capped_at_two": len(urls) == 2, # the extraction limit, not proof a 3rd video exists
"warning": f"{len(non_hls)} non-.m3u8 entry(ies), re-check before assuming HLS" if non_hls else "",
}
if __name__ == "__main__":
session = new_session()
for asin in ("B0CL61F39H", "B0ONEVID01", "B0NOVIDEO1"):
row = video_report(session, asin)
flag = f" <- {row['warning']}" if row["warning"] else ""
print(f"{row['asin']:<11} videos={row['video_count']} capped_at_two={row['capped_at_two']!s:<5}{flag}")
# Errors a client must not confuse with "no video".
try:
video_report(session, "B0NOTFOUND")
except ApiError as err:
print(f"404 not found -> {err} (billed, not retried, not 'no video')")
try:
video_report(session, "B0CL61F39H", "ATVPDKIKXODER") # typo: letter O instead of zero
except ApiError as err:
print(f"free gate 400 -> {err} (never billed)")
row = video_report(session, "B0RETRY502")
print(f"502 then 200 -> videos={row['video_count']} (retried once, billed on both attempts)")
Example output (illustrative data: happy path, empty, and error scenarios)
B0CL61F39H videos=2 capped_at_two=True
B0ONEVID01 videos=1 capped_at_two=False
B0NOVIDEO1 videos=0 capped_at_two=False
404 not found -> 404: Product not found (billed, not retried, not 'no video')
free gate 400 -> 400: Invalid marketplaceId (never billed)
502 then 200 -> videos=1 (retried once, billed on both attempts)
calls= {'amazon-product-lookup': 7}
How do I convert the HLS playlist to an MP4 file?
Point ffmpeg at the playlist URL and copy the streams without re-encoding (-c copy); it downloads every segment the manifest lists and writes one MP4. Do this for each URL video_urls.py reports - at most two per listing - and number the output files since a product can have both.
save_videos.sh - every HLS video of an ASIN as numbered MP4 files
#!/usr/bin/env bash
# Save every HLS video of one ASIN as numbered MP4 files (needs curl, jq and ffmpeg).
asin=B0CL61F39H
body=$(curl -sG "https://sellermagnet-api.com/api/amazon-product-lookup" \
-H "X-Api-Key: $SELLERMAGNET_API_KEY" \
--data-urlencode "asin=$asin" \
--data-urlencode "marketplaceId=ATVPDKIKX0DER")
# An error is not "no video": stop on anything but success=true (a 404 is still billed).
if [ "$(jq -r '.success' <<<"$body" 2>/dev/null)" != "true" ]; then
echo "lookup failed: $(jq -r '.message' <<<"$body" 2>/dev/null || echo 'non-JSON response')" >&2
exit 1
fi
n=0
while read -r url; do # null or [] gives zero lines, so zero videos is a normal result
n=$((n + 1))
ffmpeg -nostdin -loglevel error -i "$url" -c copy "${asin}_video_$n.mp4"
done < <(jq -r '(.data.productInfo.videos // [])[]' <<<"$body")
echo "saved $n video(s)"
No videos is not an error
jq's .videos // [] and the Python p.get("videos") or [] both turn a missing or null field into an empty result instead of a crash. Most listings have zero gallery videos; loop over whatever count you get, including zero.

How do I play the video without saving a file?
For a web page, hand the .m3u8 URL straight to an HLS-capable player instead of downloading anything server-side: Safari's <video> tag plays it natively, and hls.js adds that support to browsers without native HLS. ffmpeg is for the cases where you need a stored file - an archive, a thumbnail frame, or feeding the video into something that only accepts MP4.

Frequently Asked Questions
What format are Amazon product videos returned in?
productInfo.videos holds HLS playlist URLs ending in .m3u8, never a direct .mp4 link. An HLS playlist points to short video segments; a browser or ffmpeg assembles them into one stream.
How many videos does the API return per product?
At most two. The extraction step that builds productInfo.videos caps the list at two gallery videos, so a listing with more still returns only the first two.
Can I download a .m3u8 file like an image URL?
No. A plain GET on the manifest only returns a short text playlist, not the video. Use an HLS-aware tool (ffmpeg, an HLS.js player) that follows the playlist to the actual media segments.
Why is productInfo.videos missing or empty?
The listing has no gallery video. Treat null and [] as the same "no video" case; neither is an error worth retrying.
Does checking for videos cost extra credits?
No. videos comes back on the same /amazon-product-lookup call as every other product field - one credit, no separate video endpoint.
Bottom line: productInfo.videos gives you up to two HLS playlist URLs on the normal lookup call, not MP4 links - detect the count with the field itself, and convert or play the manifest with an HLS-aware tool. For the image fields on the same response, see the download Amazon product images guide; the product lookup page lists every field, and a free account includes 150 credits.