Vizit Public APIAPI
Guides

Ingest a PDP

End-to-end walkthrough for submitting Amazon ASINs or products by your own id and retrieving Vizit scores.

Guides

Vizit exposes two purpose-built PDP ingestion endpoints. Both follow the same shape — PUT /v1/pdps/{identifier_type}/{value} to submit, GET the same path to poll — but they serve different integration patterns. This guide walks each end-to-end and lays out the error catalog and rate-limit posture you should plan around.

Prerequisites

Before any of the requests below will succeed, make sure you have:

  • Client credentials provisioned by Vizit for your organization and target environment (production, or dev1dev5).
  • A Bearer token obtained via the OAuth 2.0 client-credentials flow described in Authentication. Cache it until expiry — tokens are valid for 24 hours.
  • A request ID strategy. Send X-Request-Id on each request to correlate your logs with Vizit's. If you omit it, Vizit generates one and echoes it on the response.

All examples below assume $BASE_URL is the environment-appropriate hostname (e.g., https://ext.vizit.com) and $TOKEN is your current access token.

Choose the right endpoint

You want…Endpoint pairWhat you send
Vizit to scrape Amazon for you and score the result/v1/pdps/asin/{asin}Just the ASIN (and optionally a regional Amazon storefront)
To push your own image URLs and metadata (PIM-style integrations like Syndigo or Salsify)/v1/pdps/id/{id}Your own stable id plus your hero / carousel image URLs and a product_category_id (or a gtin/asin for category resolution)

Both flows return 202 Accepted immediately and score asynchronously; clients poll GET until status reaches a terminal state. Both report status with the same uppercase values: PROCESSING while scoring is in flight, then a terminal COMPLETED or ERROR.

One identifier per endpoint

Don't try to submit the same product to both endpoints. The ASIN flow scrapes Amazon to get its own image set; the by-id flow stores the images you provide. Mixing them creates duplicate PDPs in your organization's library.

Walkthrough: scrape an Amazon ASIN

The scraped-content flow is built for services teams onboarding a customer's ASIN catalog and for partner workflows that want Vizit scores across a target brand's listings without managing images themselves.

1. Submit the ASIN

curl -X PUT "$BASE_URL/v1/pdps/asin/B07XVTRJKX" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"region": "us"}'

Path parameter and body:

FieldTypeRequiredDefaultNotes
asin (path)stringYesMust match ^[A-Z0-9]{10}$ exactly. Lowercase or wrong-length values return 400 INVALID_ASIN without a retailer call.
regionstringNo"us"Amazon storefront region. Common values: us, uk, de, ca. Unsupported values return 400 INVALID_REGION.

Successful response (202 Accepted):

{
  "asin": "B07XVTRJKX",
  "region": "us",
  "pdp_id": "9b0a0c4e-3c1d-4b9e-9f08-2c7e3a1f1d77",
  "status": "PROCESSING",
  "score_url": "/v1/pdps/asin/B07XVTRJKX?region=us"
}

score_url is the URL you poll next — it includes the region so you don't have to remember the storefront the PDP was ingested under.

Submitting an ASIN that's already in your library refreshes it (re-scrapes and re-scores) rather than rejecting the request, the same behavior as the in-product refresh button.

2. Poll for scores

curl "$BASE_URL/v1/pdps/asin/B07XVTRJKX?region=us" \
  -H "Authorization: Bearer $TOKEN"

While the pipeline is in flight, the response carries status: "PROCESSING" and the score fields are null. Once scoring lands, status flips to "COMPLETED", scored_at is populated, and the full payload is available:

{
  "asin": "B07XVTRJKX",
  "region": "us",
  "pdp_id": "9b0a0c4e-3c1d-4b9e-9f08-2c7e3a1f1d77",
  "status": "COMPLETED",
  "name": "Example Product",
  "category": "Fish Oil Supplements",
  "pdp_score": 67,
  "carousel_score": 52.4,
  "hero": {
    "image_id": "...",
    "image_url": "https://...",
    "hero_score": 72.0,
    "classification": "Packshot"
  },
  "carousel_images": [
    { "image_id": "...", "position": 1, "vizit_score": 55.0, "classification": "Lifestyle" }
  ],
  "asset_mix": { "total_images": 4, "by_classification": { "Packshot": 1, "Lifestyle": 2, "Feature Highlight": 1 } },
  "scored_at": "2026-05-13T19:42:10Z"
}

End-to-end latency for a typical Amazon ASIN is on the order of tens of seconds, capped by the scrape and scoring pipeline rather than the API layer. A polling interval of 15–30 seconds balances time-to-first-result against request volume.

If the PDP is being refreshed and a prior snapshot exists, the response returns the prior scores alongside status: "PROCESSING" so your consumers can keep using the existing data while new scoring runs.

Walkthrough: submit by your own id with your own images

The caller-supplied-content flow is built for PIM partners pushing their own image sets — Syndigo, Salsify, in-house systems — where Vizit's job is to score the assets you provide, not to scrape a retailer. You key each PDP on a stable id from your own system (a SKU, PIM id, etc.).

1. Discover a product category

By-id submissions need a product category. Supply a product_category_id (below), an external_category_id, or a gtin/asin/name that Vizit can resolve a category from. To use a product_category_id, pull the valid IDs once and cache them:

curl "$BASE_URL/v1/product-categories" \
  -H "Authorization: Bearer $TOKEN"

The ASIN flow doesn't need this — categories are inferred server-side from the scrape.

2. Submit the PDP

curl -X PUT "$BASE_URL/v1/pdps/id/SKU-12345" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "hero_image_url": "https://your-cdn.example.com/products/abc/hero.jpg",
    "carousel_image_urls": [
      "https://your-cdn.example.com/products/abc/carousel1.jpg",
      "https://your-cdn.example.com/products/abc/carousel2.jpg"
    ],
    "product_category_id": "5b1a7c30-7e9b-4f23-9b6f-8c12a18d3a40",
    "name": "Example Product",
    "retailer": "amazon_us"
  }'

Vizit downloads the images server-side, stores them, and kicks off the standard scoring pipeline. The id in the path can be any stable identifier from your own system (a SKU, PIM id, etc.), up to 512 characters.

Successful response (202 Accepted):

{
  "id": "SKU-12345",
  "pdp_id": "...",
  "status": "PROCESSING",
  "error_code": null,
  "score_url": "/v1/pdps/id/SKU-12345?retailer=amazon_us"
}

3. Poll for scores

Same polling pattern as the ASIN flow, and the response shape mirrors /v1/pdps/asin/{asin} with id instead of asin and a retailer field instead of region. Pass the optional retailer query parameter (defaulting to amazon_us) to disambiguate the same id submitted under different retailers; the score_url above already carries it.

curl "$BASE_URL/v1/pdps/id/SKU-12345?retailer=amazon_us" \
  -H "Authorization: Bearer $TOKEN"

Error cheat sheet

Every non-2xx response follows the standard error shape: error_code, detail, status_code, request_id. Branch on error_code, never on detail.

Validation errors (400, 413)

error_codeEndpointWhat it meansLikely caller action
INVALID_ASINASINASIN doesn't match ^[A-Z0-9]{10}$.Uppercase and validate the format before sending. Don't retry the same value.
INVALID_REGIONASINThe body's region isn't a supported Amazon storefront.Use one of the documented region codes (us, uk, de, ca, …).
INVALID_IDby idThe path id is empty or longer than 512 characters.Send a non-empty id of at most 512 characters.
INVALID_RETAILERby idThe retailer value isn't a supported retailer_region key.Use amazon_us (default) or another documented retailer region.
NO_DEFAULT_PORTFOLIOby idYour organization has no default portfolio configured.Contact Vizit to configure a default portfolio.
CATEGORY_NOT_IN_ORG_ICPASINThe scraped category isn't in your organization's ICP (Ideal Customer Profile) configuration.Confirm category coverage with Vizit; this PDP can't be scored under the current configuration.
CATEGORY_NOT_FOUNDby idThe supplied product_category_id doesn't exist or is outside your organization's ICP.Use a product_category_id in your org's ICP, or omit it and supply gtin/asin/name for inference.
EXTERNAL_CATEGORY_UNMAPPEDby idThe supplied external_category_id couldn't be mapped to a Vizit category.Use a mappable external_category_id, or supply product_category_id/gtin/asin/name instead.
CATEGORY_UNRESOLVEDby idNo category was supplied and none could be inferred from gtin, asin, or name.Supply a product_category_id (or an external_category_id/gtin/asin/name Vizit can resolve).
NO_VALID_CATEGORYASINThe retailer scrape didn't yield a category Vizit could resolve.Vizit logs the gap automatically; retry later if the category coverage is updated.
TOO_MANY_IMAGES (413)by idcarousel_image_urls exceeded 20 entries.Trim the carousel to 20 or fewer URLs.
IMAGE_DOWNLOAD_FAILEDby idOne or more submitted image URLs couldn't be fetched. extra.failed_urls lists each {url, reason}.Inspect extra.failed_urls and fix the source URLs (auth-expired, 404, wrong content-type, etc.).

Resource errors (404)

error_codeWhenWhat to do
ASIN_NOT_FOUND_AT_RETAILERThe ASIN couldn't be resolved at the retailer.Verify the ASIN exists on the target storefront. Don't retry the same value.
PDP_NOT_FOUNDGET for a PDP your organization hasn't ingested, or one queried under the wrong region (ASIN) or retailer (by id).For ASIN GETs, check the region query parameter; for by-id GETs, confirm the PDP was previously PUT under the same id and organization, and pass the same retailer (the by-id GET defaults to amazon_us).

Auth and rate-limit errors

StatusHeader / CodeAction
401 UnauthorizedRequest a fresh access token and retry once. See Authentication.
403 ForbiddenThe token is valid but doesn't have permission for this resource. Contact Vizit.
429 Too Many RequestsRetry-After headerBack off for Retry-After seconds, then resume. Use exponential backoff for repeated 429s.

Upstream errors (5xx)

error_codeStatusWhat to do
SCRAPE_TIMEOUT504Retailer scrape timed out. Retry with exponential backoff.
SCRAPE_UPSTREAM_ERROR502Retailer scrape failed. Retry with exponential backoff; if it persists for hours, contact Vizit.

See Errors for the full shared error format and HTTP status reference.

Rate limits and retries

  • Per-organization rate limits are enforced at the API gateway. The exact tier is set when Vizit provisions your credentials; treat a dozen submissions per minute per organization as a safe baseline unless you've been told otherwise.
  • Exceeding the limit returns 429 Too Many Requests with a Retry-After header.
  • Retry 429 and 5xx responses with exponential backoff (start at ~5s, double on each retry, cap at ~60s).
  • Do not retry 4xx responses other than 429 until you've fixed the underlying request — the same input will fail the same way.

What's next

On this page