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 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"
}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_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/name 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 supply product_category_id/gtin/asin/name instead. |
CATEGORY_UNRESOLVED | by id | No 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_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. |
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 credentials, token acquisition, and security guidance: Authentication.
- For the cross-cutting error response format: Errors.