# Product schema (/docs/schema/product) {/* AUTO-GENERATED. Do not hand-edit. Source: packages/social-api/src/docs/generate-schema-pages.ts Regenerate with: pnpm -F @repo/social-api generate:docs Field tables derive from the canonical Zod schemas (packages/social-api/src/schemas/canonical.ts); availability + equivalence derive from the registry fieldMaps. */} Product schema [#product-schema] Every SocialCrawl endpoint that returns a product gives you this exact shape, whatever the source platform. Write your parser once and the same code reads product data from every platform below. That is the unified schema: one contract instead of a dozen raw upstream formats. Field reference [#field-reference] | Field | Type | Nullable | Description | | ---------------- | -------------------- | -------- | ------------------------------------------------------------------------- | | `id` | `string` | No | Platform product ID (Amazon ASIN / Google Shopping product id) | | `url` | `string` | Yes | Direct URL to the product page | | `title` | `string` | Yes | Product title | | `description` | `string` | Yes | | | `seller` | `string` | Yes | | | `brand` | `string` | Yes | Brand name (cleaned). Null when the platform exposes a seller instead. | | `price.current` | `integer` | Yes | | | `price.original` | `integer` | Yes | | | `price.currency` | `string` | Yes | | | `rating.average` | `integer` | Yes | | | `rating.count` | `integer` | Yes | | | `image_urls` | `string or string[]` | Yes | Primary image URL, or an array of image URLs for products with a gallery. | | `availability` | `string` | Yes | Stock/availability string when surfaced | | `reviews_count` | `integer` | Yes | | | `features` | `string[]` | Yes | | | `specifications` | `object[]` | Yes | | | `variations` | `object[]` | Yes | | Extension fields (`ext`) [#extension-fields-ext] Platform-specific passthrough. Each field is present only on the platforms that expose it and is absent everywhere else, so treat every `ext.*` field as optional. These carry the richer, per-platform signals the unified leaves cannot hold, and the join keys that chain endpoints together. | Field | Type | Nullable | Description | | ----------------------------------------------------------------- | ---------- | -------- | ----------- | | `ext.gid` | `string` | Yes | | | `ext.data_docid` | `string` | Yes | | | `ext.pvf` | `string` | Yes | | | `ext.seller_id` | `string` | Yes | | | `ext.sold_count` | `integer` | Yes | | | `ext.catalog_id` | `string` | Yes | | | `ext.requested_id` | `string` | Yes | | | `ext.rating_distribution.star_1` | `integer` | Yes | | | `ext.rating_distribution.star_2` | `integer` | Yes | | | `ext.rating_distribution.star_3` | `integer` | Yes | | | `ext.rating_distribution.star_4` | `integer` | Yes | | | `ext.rating_distribution.star_5` | `integer` | Yes | | | `ext.store_inventory` | `object[]` | Yes | | | `ext.condition` | `string` | Yes | | | `ext.available_quantity` | `integer` | Yes | | | `ext.watchers` | `integer` | Yes | | | `ext.sold_at` | `string` | Yes | | | `ext.sold_caption` | `string` | Yes | | | `ext.buying_format` | `string` | Yes | | | `ext.seller_reputation.feedback_percentage` | `integer` | Yes | | | `ext.seller_reputation.feedback_count` | `integer` | Yes | | | `ext.seller_reputation.top_rated` | `boolean` | Yes | | | `ext.seller_reputation.items_sold` | `integer` | Yes | | | `ext.seller_reputation.joined` | `string` | Yes | | | `ext.seller_reputation.url` | `string` | Yes | | | `ext.seller_reputation.detailed_ratings.accurate_description` | `integer` | Yes | | | `ext.seller_reputation.detailed_ratings.reasonable_shipping_cost` | `integer` | Yes | | | `ext.seller_reputation.detailed_ratings.shipping_speed` | `integer` | Yes | | | `ext.seller_reputation.detailed_ratings.communication` | `integer` | Yes | | Platform availability [#platform-availability] 7 platforms return the `Product` shape. `id` are never null on any platform. The fields below vary: **yes** means the platform populates it (the value may still be null); a blank means the platform never provides it, so it is always null.