Vizit Public APIAPI
Guides

How the Score Is Computed

How a PDP score adds up from its five components, and how to read the score breakdown on PDP score responses.

Guides

Every COMPLETED PDP score response from GET /v1/pdps/id/{id} or GET /v1/pdps/asin/{asin} says how its pdp_score was computed in score_method. This guide explains the two methods, the five components of a formula score, and how to read the score_breakdown that comes with it.

Two scoring methods

score_methodWhat you get
FORMULApdp_score is the sum of five weighted components. score_breakdown shows each component's score, weight, and points, scoring_version names the version of the scoring rules behind it, and formula_version gives the date its weights took effect; any of these can be null (see below). The four penalty fields are null.
LEGACYpdp_score comes from Vizit's earlier scoring method. There is no breakdown: score_breakdown, scoring_version, and formula_version are null, and the penalty fields (score_penalty, image_count_penalty, image_mix_penalty, image_order_penalty) say what lowered the score.

Responses and exports can carry either method, so handle both. While status is PROCESSING or ERROR, score_method, scoring_version, formula_version, score_breakdown, unavailable_reason, and withheld_reason are all null.

Which method is my score?

Read score_method on each response. Don't assume it from your account: while an organization moves from LEGACY to FORMULA, some of its PDPs can carry one method and some the other, and a PDP switches the next time it's scored.

score = response.json()

if score["status"] != "COMPLETED":
    ...  # still scoring, or failed: no score yet
elif score["unavailable_reason"] == "SCORE_WITHHELD":
    print("withheld:", score["withheld_reason"])  # display as unscored, not 0
elif score["score_method"] == "FORMULA":
    breakdown = score["score_breakdown"]  # can be null, see below
    for name, part in (breakdown or {}).get("components", {}).items():
        print(name, part["points"], "points,", part["points_to_go"], "to go")
else:  # LEGACY
    penalties = [f for f in ("score_penalty", "image_count_penalty",
                             "image_mix_penalty", "image_order_penalty") if score[f]]
    print("lowered by:", penalties)

A FORMULA score can come without a breakdown; When parts of the breakdown are missing explains when.

The five components

A formula score looks at five things about the listing:

ComponentWhat it measures
heroThe hero image's score.
carouselThe average score of the scored carousel images.
orderWhether the carousel images are in the order that earns full credit. With fewer than two scored carousel images, order scores 0.
image_countHow many images besides the hero (carousel images, including informational ones) the listing has against the count its category needs for full credit. Images beyond that count lower this component too.
asset_mixHow well the images cover the asset types in the category's ideal content mix.

Utility, weight, and points

Each component in score_breakdown.components carries the same numbers:

  • utility — the component's own score, a whole number from 0 to 100.
  • weight — the component's share of the PDP score, as a fraction from 0 to 1. The five weights add up to 1. Weights vary by category, so the same utility can be worth more in one category than in another.
  • points — what the component adds to pdp_score. When shown, the five points add up to pdp_score.
  • points_to_go — what the component could still add. For each component, points plus points_to_go is its weight as a whole-number share of 100, so the five points_to_go add up to 100 - pdp_score.

Points are whole numbers, rounded so that they add up exactly to pdp_score. That means a component's points can differ slightly from weight × utility, and a component whose utility shows as 100 can still have a point to go.

Here is a listing that scores 67:

Componentutilityweightpointspoints_to_go
hero720.30228
carousel520.251312
order900.1091
image_count670.15105
asset_mix670.20137
Total1.006733

The largest points_to_go shows where the listing has the most to gain: here, a stronger carousel (12 points) and a better hero image (8 points).

Tags and details

Each component also has a tag:

  • COMPUTED — the component was scored from the listing's content.
  • ABSENT_CONTENT — there was nothing to score for this component, for example a listing with no carousel images, or a category that sets no content mix target. The component's utility is 0.

Three components add a detail object that explains their utility. hero and carousel never have one, so their detail is always null.

"order": {
  "utility": 90, "weight": 0.1, "points": 9, "points_to_go": 1, "tag": "COMPUTED",
  "detail": { "is_sorted": false }
},
"image_count": {
  "utility": 67, "weight": 0.15, "points": 10, "points_to_go": 5, "tag": "COMPUTED",
  "detail": { "count": 4, "required": 6, "short_by": 2 }
},
"asset_mix": {
  "utility": 67, "weight": 0.2, "points": 13, "points_to_go": 7, "tag": "COMPUTED",
  "detail": {
    "types": [
      { "key": "feature_benefit", "display_name": "Feature/Benefit", "required": 2, "actual": 1, "short_by": 1 },
      { "key": "lifestyle", "display_name": "Lifestyle", "required": 2, "actual": 2, "short_by": 0 },
      { "key": "multi_pack", "display_name": "Multi-Pack", "required": 1, "actual": 0, "short_by": 1 },
      { "key": "size_and_scale", "display_name": "Size and Scale", "required": 1, "actual": 1, "short_by": 0 }
    ]
  }
}
  • order.detail.is_sorted is true only when the order earns full credit. A utility that rounds to 100 can still come with is_sorted: false.
  • image_count.detail compares count, the listing's images besides the hero (carousel images, including informational ones), with the required count. short_by is how many images are still missing; it is 0 once the listing has enough, including when it has more than required.
  • asset_mix.detail.types has one entry per asset type the category's ideal content mix asks for, with the required and actual image counts and how many the listing is short_by. display_name matches the classification labels on images; key is a stable identifier to match on in code.

A detail is null when its component isn't COMPUTED or the counts can't be shown.

When parts of the breakdown are missing

Build your integration so each of these reads cleanly:

  • No breakdown. score_breakdown is null for every LEGACY score, and for a FORMULA score when the breakdown can't be shown. Unless the score is withheld (below), pdp_score is still valid. scoring_version and formula_version can also be null on a FORMULA score.
  • No points. When points can't be shown, points and points_to_go are null on every component. utility, weight, and tag are still there.
  • Withheld score. Sometimes scoring finishes but no formula score can be produced for the listing. The response is then COMPLETED with score_method: "FORMULA", pdp_score: null, score_breakdown: null, and unavailable_reason: "SCORE_WITHHELD"; vizit_certified and score_change are null too. withheld_reason says why, so you can tell your users what to fix. The hero and carousel image scores and the content mix are still in the response. Show the listing as unscored rather than as 0.
{
  "status": "COMPLETED",
  "pdp_score": null,
  "vizit_certified": null,
  "score_method": "FORMULA",
  "score_breakdown": null,
  "unavailable_reason": "SCORE_WITHHELD",
  "withheld_reason": "HERO_IMAGE_NOT_SCORED"
}
withheld_reasonWhat it meansWhat to do
HERO_IMAGE_MISSINGThe listing has no hero image.Add a hero image.
HERO_IMAGE_NOT_SCOREDThe hero image could not be scored.Check the image URL is reachable, or submit the PDP again.
CAROUSEL_IMAGE_NOT_SCOREDA carousel image could not be scored.Check the carousel image URLs, or submit the PDP again.
CAROUSEL_IMAGES_MISSINGThe listing has no carousel images that can be scored.Add carousel images.
IMAGE_TYPES_UNRESOLVEDSome image types could not be determined yet.Try again later.
CATEGORY_NOT_SUPPORTEDThe product category is not set up for scoring.Contact Vizit.
SCORING_ERRORScoring failed for another reason.Submit the PDP again; contact Vizit if it persists.

New codes may be added, so treat a code you don't recognize like SCORING_ERROR.

In the CSV export, a withheld score leaves listing_score empty and fills the withheld_reason column.

When weights change

Vizit updates its scoring weights from time to time. New weights apply the next time a PDP is scored; an existing score keeps its weights until the PDP is scored again. formula_version tells you which weights produced a score, as the date they took effect (for example 2026-09-24). listing_score_at_ingest is the first score a PDP gets after its scoring weights or setup change, and score_change is measured from it, so score_change is 0 until the PDP is scored again. A new formula_version always restarts it; other scoring setup changes can restart it too while formula_version stays the same.

In the CSV export

subject: "pdps" exports add score_method, scoring_version, and formula_version columns, plus one utility column per component (hero_utility, carousel_utility, order_utility, image_count_utility, asset_mix_utility) that is filled on FORMULA rows. See PDP scoring columns in the Export Scores guide.

What's next

On this page