An Amazon listing audit compares your product page with the listings that already win your main keyword. /api/amazon-search returns the first results page; /api/amazon-product-lookup returns each listing's title, bullet points, images, videos, A+ content flag and ratings. The script below checks your title against Amazon's January 2025 title rules and flags where your listing has fewer images, bullet points or ratings than the top five organic results, or lacks the video or A+ content most of them have - seven credits per audit.
Key Takeaways
- An audit against five competitors costs seven credits: one search and one lookup per listing.
- Sponsored results are skipped, so the benchmark is the listings that rank organically for the keyword.
- Amazon's title rules since January 21, 2025: at most 200 characters in most categories, no ! $ ? _ { } ^ ¬ ¦ outside a brand name, no word more than twice except prepositions, articles and conjunctions.
- Image, bullet and ratings counts are compared with the competitors' median; video and A+ content with how many competitors have them.
- The lookup reports at most two videos, and its A+ flag is also true for a brand story or product documents.
What should an Amazon listing audit check?
Two kinds of things: rules and gaps. Rules come from Amazon - since January 21, 2025, a title may not exceed 200 characters in most categories, may not contain the characters ! $ ? _ { } ^ ¬ ¦ unless they are part of the brand name, and may not repeat a word more than twice; prepositions, articles and conjunctions are exempt. Gaps come from your competitors: fewer images, fewer bullet points, no video or no A+ content where most of them have it.
| Check | Field | Finding when |
|---|---|---|
| Title length | title | Over 200 characters |
| Special characters | title | ! $ ? _ { } ^ ¬ ¦ outside the brand |
| Repeated words | title | A word more than twice |
| Keyword | title | A keyword word is missing |
| Images | images | Fewer than the competitor median |
| Bullet points | bulletPoints | Fewer than the competitor median |
| Video | videos | None, while most competitors have one |
| A+ content | hasAPlusContent | None, while most competitors have it |
| Ratings | reviews.totalReviews | Fewer than the competitor median |
How do I audit a listing against competitors in Python?
Search the keyword, look up your listing and the first five organic results, then compare. audit() turns one lookup into the numbers above; findings() applies the title rules and the competitor medians and prints your own position for the keyword. A competitor whose lookup fails is skipped and the next organic result takes its place, up to two times; a 401 or 403 stops the run. get_with_retry() comes from retry.py, new_session() and ApiError from sellermagnet.py in the Python quickstart.
listing_audit.py - your listing against the top five organic results
"""Listing audit: your listing against the top organic results for your main keyword."""
import re
import statistics
import sys
import requests
from retry import get_with_retry
from sellermagnet import ApiError, new_session
MARKETPLACE, KEYWORD, YOUR_ASIN = "ATVPDKIKX0DER", "playstation 5 slim", "B0CL61F39H"
COMPETITORS = 5
BANNED = set("!$?_{}^¬¦") # Amazon title rules (Jan 2025): allowed only in a brand name
SMALL_WORDS = {"a", "an", "the", "and", "or", "but", "nor", "so", "yet", "than", "for", "with", "without", "of",
"in", "on", "at", "to", "by", "from", "as", "into", "per", "over", "under", "up", "about",
"between", "through", "after"} # English; the rule exempts prepositions, articles, conjunctions
def words(text: str) -> list[str]:
return [w for w in re.findall(r"[^\W_]+", text.lower()) if len(w) > 1 and not w.isdigit()]
def audit(p: dict) -> dict:
title = p.get("title") or ""
counted = [w for w in words(title) if w not in SMALL_WORDS]
bullets = p.get("bulletPoints") or []
reviews = p.get("reviews") or {}
return {"asin": p.get("asin"), "title_chars": len(title), "bullets": len(bullets),
"images": len(p.get("images") or []), "video": bool(p.get("videos")),
"a_plus": bool(p.get("hasAPlusContent")), "rating": reviews.get("averageRating"),
"ratings": reviews.get("totalReviews") or 0,
"banned_chars": "".join(sorted(BANNED & set(title))),
"repeated": sorted({w for w in counted if counted.count(w) > 2}),
"missing_keywords": [w for w in dict.fromkeys(words(KEYWORD)) if w not in words(title)]}
def findings(you: dict, rivals: list[dict]) -> list[str]:
out = []
if you["title_chars"] > 200:
out.append(f"title has {you['title_chars']} characters: most categories allow 200")
if you["banned_chars"]:
out.append(f"title contains {you['banned_chars']}: allowed only as part of the brand name")
if you["repeated"]:
out.append(f"title repeats {', '.join(you['repeated'])} more than twice")
if you["missing_keywords"]:
out.append(f"title lacks the keyword word(s) {', '.join(you['missing_keywords'])}")
for key, label in (("images", "images"), ("bullets", "bullet points"), ("ratings", "ratings")):
values = [r[key] for r in rivals if key != "ratings" or r[key]] # 0 ratings can be an unread count
median = statistics.median_low(values) if values else None
if median is not None and you[key] < median:
out.append(f"{label}: {you[key]:,} vs a median of {median:,} among the top {len(rivals)}")
for key, label in (("video", "video"), ("a_plus", "A+ content")):
have = sum(r[key] for r in rivals)
if not you[key] and have > len(rivals) / 2:
out.append(f"no {label}: {have} of {len(rivals)} competitors have it")
return out
def lookup(session, asin: str) -> dict:
p = get_with_retry(session, "amazon-product-lookup", asin=asin, marketplaceId=MARKETPLACE)
return audit(p["productInfo"])
if __name__ == "__main__":
session = new_session()
try:
results = get_with_retry(session, "amazon-search", q=KEYWORD, marketplaceId=MARKETPLACE,
count=20)["searchResults"]
you = lookup(session, YOUR_ASIN)
except (ApiError, requests.RequestException) as err:
sys.exit(f"stopped: {err}") # e.g. a 404 on YOUR_ASIN
rivals, failed = [], 0
for asin in dict.fromkeys(r.get("asin") for r in results if not r.get("sponsored")):
if len(rivals) == COMPETITORS or failed == 2:
break
if asin and asin != YOUR_ASIN:
try:
rivals.append(lookup(session, asin))
except (ApiError, requests.RequestException) as err:
if getattr(err, "status", None) in (401, 403):
sys.exit(f"stopped: {err}") # key or credits: every further call fails too
failed += 1 # the next organic result takes its place
position = next((r.get("position") for r in results if r.get("asin") == YOUR_ASIN and not r.get("sponsored")),
None)
print(f"your position for '{KEYWORD}': {position or 'not among the priced results on page 1'}")
for r in [you, *rivals]:
print(f"{r['asin']} title {r['title_chars']:>3} bullets {r['bullets']} images {r['images']:>2} "
f"video {'yes' if r['video'] else 'no ':<3} A+ {'yes' if r['a_plus'] else 'no ':<3} "
f"{r['rating'] or '-'} ({r['ratings']:,})")
print("\n".join(f"- {f}" for f in findings(you, rivals)) if rivals
else f"no competitors to compare ({failed} lookups failed)")
Audit output (illustrative data; your title is deliberately broken)
your position for 'playstation 5 slim': 2
B0CL61F39H title 208 bullets 3 images 4 video no A+ no 4.6 (1,200)
B0RIVAL001 title 22 bullets 5 images 7 video yes A+ yes 4.7 (5,400)
B0RIVAL002 title 17 bullets 5 images 6 video yes A+ yes 4.5 (3,100)
B0RIVAL004 title 18 bullets 5 images 8 video yes A+ yes 4.8 (9,800)
B0RIVAL005 title 18 bullets 5 images 5 video no A+ yes 4.4 (800)
B0RIVAL006 title 17 bullets 4 images 7 video yes A+ yes 4.6 (4,100)
- title has 208 characters: most categories allow 200
- title contains !: allowed only as part of the brand name
- title repeats console, gaming more than twice
- images: 4 vs a median of 7 among the top 5
- bullet points: 3 vs a median of 5 among the top 5
- ratings: 1,200 vs a median of 4,100 among the top 5
- no video: 4 of 5 competitors have it
- no A+ content: 5 of 5 competitors have it
For a quick look at one listing - yours or a competitor's - jq prints the same counts from a single lookup.
Title length, bullets, images, video and A+ for one listing
curl -sG "https://sellermagnet-api.com/api/amazon-product-lookup" \
-H "X-Api-Key: $SELLERMAGNET_API_KEY" --max-time 120 \
--data-urlencode "asin=B0CL61F39H" \
--data-urlencode "marketplaceId=ATVPDKIKX0DER" \
| jq -r 'if .success then .data.productInfo
| "title \(.title | length) chars, \(.bulletPoints | length) bullets, \(.images | length) images, video: \((.videos // []) | length > 0), A+: \(.hasAPlusContent)"
else error(.message) end'

Which competitors should the benchmark use?
The listings that rank organically for the keyword that matters most to you. The search reads the first results page only and skips products without a displayed price; sponsored results are marked and left out, because an ad position says little about the listing itself. Run the audit for two or three keywords when your product sells under different names - the keyword rank tracking guide shows where you rank for each.

What the audit cannot judge
- Image quality. The lookup lists image URLs, not backgrounds or resolution; the product image guide shows how to download them and check sizes.
- The brand-name exemption. A
!in a brand name is allowed; the script flags it and you decide. - Other languages. The exempt small words are English, and the search marks sponsored results by their label markup, with a text fallback only in English and German; check both on other stores.
- Counts that depend on the page. The bullet count comes from the feature bullets or, where Amazon shows it, the product-facts block; a ratings count of 0 can also mean it could not be read, so the script leaves zeros out of the median.
- What only Seller Central shows. Backend search terms, suppressed listings, main-image compliance and Amazon's own listing quality reports are not in product data.
- Copy quality. Counts say that a competitor has five bullet points, not that they are good ones.
Competitors change their listings too. Once the audit has shown which listings set the bar, the listing change monitor tells you when one of them changes its title, bullet points, images or A+ content.
Frequently Asked Questions
How do I audit an Amazon listing?
Check the title against Amazon's title rules, then compare images, bullet points, video, A+ content and ratings with the listings that rank organically for your main keyword.
What are Amazon's title requirements?
Since January 21, 2025: at most 200 characters in most categories, no ! $ ? _ { } ^ ¬ ¦ unless part of the brand name, and no word more than twice, except prepositions, articles and conjunctions.
How many credits does an audit cost?
Seven if every lookup succeeds: one search and six lookups. Failed attempts are billed too - a 404 once, a 502 or 503 up to four times through get_with_retry() - and each replacement competitor costs one more.
Why are sponsored results skipped?
Sponsored placements are paid for; the benchmark uses the listings Amazon ranks organically for the keyword.
Does the audit check image quality?
No. The lookup returns image URLs; background and resolution need the images themselves, which you can download and inspect.
Bottom line: one search and six lookups show whether your title follows Amazon's rules and where your listing has less than the ones that rank - fix those first. A free account includes 150 credits, enough for 21 audits.