Ingest a PDP
End-to-end walkthrough for submitting Amazon ASINs or products by your own id and retrieving Vizit scores.
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
dev1–dev5). - 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-Idon 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 pair | What 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 external_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:
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
asin (path) | string | Yes | — | Must match ^[A-Z0-9]{10}$ exactly. Lowercase or wrong-length values return 400 INVALID_ASIN without a retailer call. |
region | string | No | "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",
"score_method": "LEGACY",
"scoring_version": null,
"formula_version": null,
"score_breakdown": null,
"unavailable_reason": null,
"withheld_reason": null
}score_method says how pdp_score was computed. A FORMULA score also carries a score_breakdown of its five components when one can be shown; see How the score is computed for how to read it.
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.
While a PDP is being refreshed, status is "PROCESSING" and the score fields are null until the new scores land.
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 that Vizit can resolve a category from. Optional name and description further enrich resolution when an external_category_id label needs mapping. 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": "Vizit Fish Oil 1000mg",
"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",
"category_match_id": "5b1a7c30-7e9b-4f23-9b6f-8c12a18d3a40",
"category_match_name": "Fish Oil Supplements"
}category_match_id / category_match_name report the Vizit product category the PDP resolved to — always populated, whether you supplied the category or Vizit resolved it.
If you don't have a Vizit product_category_id, send your own external_category_id label instead. Here name and description earn their keep: Vizit uses them to map the label to a Vizit category (description is used only for this and is never persisted):
curl -X PUT "$BASE_URL/v1/pdps/id/SKU-67890" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"hero_image_url": "https://your-cdn.example.com/products/def/hero.jpg",
"carousel_image_urls": ["https://your-cdn.example.com/products/def/carousel1.jpg"],
"external_category_id": "my-catalog/fish-oil",
"name": "Vizit Fish Oil 1000mg",
"description": "High-potency omega-3 fish oil softgels, 1000 mg per serving.",
"retailer": "amazon_us"
}'The response has the same shape; category_match_id / category_match_name report the Vizit category your label resolved to.
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_code | Endpoint | What it means | Likely caller action |
|---|---|---|---|
INVALID_ASIN | ASIN | ASIN doesn't match ^[A-Z0-9]{10}$. | Uppercase and validate the format before sending. Don't retry the same value. |
INVALID_REGION | ASIN | The body's region isn't a supported Amazon storefront. | Use one of the documented region codes (us, uk, de, ca, …). |
INVALID_ID | by id | The path id is empty or longer than 512 characters. | Send a non-empty id of at most 512 characters. |
INVALID_RETAILER | by id | The retailer value isn't a supported retailer_region key. | Use amazon_us (default) or another documented retailer region. |
NO_DEFAULT_PORTFOLIO | by id | Your organization has no default portfolio configured. | Contact Vizit to configure a default portfolio. |
CATEGORY_NOT_IN_ORG_ICP | ASIN | The 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_FOUND | by id | The 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 for inference. |
EXTERNAL_CATEGORY_UNMAPPED | by id | The supplied external_category_id couldn't be mapped to a Vizit category. | Use a mappable external_category_id or a product_category_id. A gtin/asin is tried during resolution even while the label is present, so you can add one without dropping the label. If you already sent one and it didn't resolve, dropping the external_category_id only changes the error to CATEGORY_UNRESOLVED — it won't make it resolve. name/description only enrich the label mapping. |
CATEGORY_REQUIRED | by id | No category signal was supplied — none of product_category_id, external_category_id, gtin, or asin — and this id has no prior submission whose category could be reused. | Supply at least one of those (or re-PUT an id you've already submitted under the same retailer) so Vizit has a category to resolve. |
CATEGORY_UNRESOLVED | by id | A gtin or asin was supplied but no category could be inferred from it. | Supply a product_category_id or an external_category_id Vizit can map. |
NO_VALID_CATEGORY | ASIN | The 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 id | carousel_image_urls exceeded 20 entries. | Trim the carousel to 20 or fewer URLs. |
IMAGE_DOWNLOAD_FAILED | by id | One 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_code | When | What to do |
|---|---|---|
ASIN_NOT_FOUND_AT_RETAILER | The ASIN couldn't be resolved at the retailer. | Verify the ASIN exists on the target storefront. Don't retry the same value. |
PDP_NOT_FOUND | GET 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
| Status | Header / Code | Action |
|---|---|---|
401 Unauthorized | — | Request a fresh access token and retry once. See Authentication. |
403 Forbidden | — | The token is valid but doesn't have permission for this resource. Contact Vizit. |
429 Too Many Requests | Retry-After header | Back off for Retry-After seconds, then resume. Use exponential backoff for repeated 429s. |
Upstream errors (5xx)
error_code | Status | What to do |
|---|---|---|
SCRAPE_TIMEOUT | 504 | Retailer scrape timed out. Retry with exponential backoff. |
SCRAPE_UPSTREAM_ERROR | 502 | Retailer scrape failed. Retry with exponential backoff; if it persists for hours, contact Vizit. |
CATEGORY_RESOLUTION_UNAVAILABLE | 503 | by id — only while Vizit is resolving an external_category_id label on your behalf: that resolution is temporarily unavailable. The PDP was not scored under a wrong category. Retry with backoff; this is transient. |
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 Requestswith aRetry-Afterheader. - Retry
429and5xxresponses with exponential backoff (start at ~5s, double on each retry, cap at ~60s). - Do not retry
4xxresponses other than429until you've fixed the underlying request — the same input will fail the same way.
What's next
- The full operation contracts are in the API Reference:
- For how
pdp_scoreadds up: How the score is computed. - For credentials, token acquisition, and security guidance: Authentication.
- For the cross-cutting error response format: Errors.