# List product reviews

> Returns product reviews and their submitted content.

Breadcrumb: Storefront API › List product reviews
Docs index: https://docs.cevoid.com/developers/llms.txt

`GET https://api.widget.cevoid.com/v1/products/{product_id}/reviews`

Returns product reviews and their submitted content.

## Authorizations

Bearer token in the Authorization header: `Authorization: Bearer <key>` (Cevoid publishable key).

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `product_id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rev_abc123`. |

## Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `market` | `string` | No | Presentation market. Defaults to the workspace default; does not filter collection origin. Length: `1 to 200`. Example: `mar_abc123`. |
| `limit` | `integer` | No | Maximum page size; does not limit total traversal. Default: `25`. Range: `1 <= x <= 100`. Example: `25`. |
| `cursor` | `string` | No | Opaque next-page cursor. Keep filters and market fixed. Maximum length: `3000`. Example: `eyJ2IjoxLCJjdXJzb3IiOiJyZXZfYWJjMTIzIn0`. |
| `sort` | `enum<string>` | No | Latest and oldest order by creation in Cevoid, not submission date. Highest and lowest order by rating, then newest creation. Ties use stable review identity. Default: `latest`. Allowed values: `latest`, `oldest`, `highest`, `lowest`. Example: `latest`. |
| `rating` | `integer` | No | Return reviews with exactly this star rating, from 1 to 5. Range: `1 <= x <= 5`. Example: `5`. |
| `with_media` | `enum<string>` | No | True selects reviews with at least one non-processing asset that is neither pending nor declined. False selects reviews without such assets. Allowed values: `true`, `false`. Example: `true`. |
| `verified` | `enum<string>` | No | True selects verified purchases. False selects every other purchase-verification state. This does not filter author identity verification. Allowed values: `true`, `false`. Example: `true`. |
| `submitted_after` | `string<date-time>` | No | Include reviews submitted at or after this UTC timestamp, using the original submission date for imports. Example: `2026-09-01T10:00:00.000Z`. |
| `submitted_before` | `string<date-time>` | No | Include reviews submitted strictly before this UTC timestamp, using the original submission date for imports. Example: `2026-09-01T10:00:00.000Z`. |
| `include` | `enum<string>` | No | Include compact original content alongside localized fields. Review reads include reviews, public replies, visible media accessibility text, and returned questions. Summary reads include returned questions only. Allowed value: `localization`. Example: `localization`. |
| `pinned` | `enum<string>` | No | Filter product reviews by whether they are pinned. Omit to return both pinned and unpinned reviews. Existing sorting still applies. Allowed values: `true`, `false`. Example: `true`. |

## Response

### 200: Successful response

`application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | `object` | Yes |  |
| `data.reviews` | `object[]` | Yes |  |
| `data.reviews[].id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rev_abc123`. |
| `data.reviews[].rating` | `integer` | Yes | Score from 1 to 5. Range: `1 <= x <= 5`. Example: `5`. |
| `data.reviews[].title` | `string` | No | Reviewer-supplied headline. In localized responses, title and body both use source text unless a complete translation is available. Example: `Comfortable everyday fit`. |
| `data.reviews[].body` | `string` | No | Reviewer-supplied text. Absent for star-only reviews. In localized responses, title and body both use source text unless a complete translation is available. Example: `Comfortable and true to size.`. |
| `data.reviews[].language` | `string` | Yes | BCP-47 language of the original submission, even when the returned text is translated. Example: `en`. |
| `data.reviews[].purchase_verification` | `enum<string>` | Yes | Purchase evidence state behind verified-buyer display. Allowed values: `VERIFIED`, `UNVERIFIED`. Example: `UNVERIFIED`. |
| `data.reviews[].is_incentivized` | `boolean` | Yes | Whether a reward influenced submission. Example: `false`. |
| `data.reviews[].is_insider` | `boolean` | Yes | Whether the reviewer is brand-connected. Example: `false`. |
| `data.reviews[].channel` | `enum<string>` | Yes | How the review entered Cevoid. Allowed values: `REQUEST`, `WIDGET`, `LINK`, `IMPORT`, `SYNDICATED_IN`. Example: `IMPORT`. |
| `data.reviews[].submitted_at` | `string<date-time>` | No | ISO 8601 timestamp when submission completed. Example: `2026-09-01T10:00:00.000Z`. |
| `data.reviews[].published_at` | `string<date-time>` | No | ISO 8601 timestamp when it first became public. Example: `2026-09-01T10:00:00.000Z`. |
| `data.reviews[].edited_at` | `string<date-time>` | No | Last reviewer edit; unrelated processing does not change this timestamp. Example: `2026-09-01T10:00:00.000Z`. |
| `data.reviews[].reviewer` | `object` | Yes |  |
| `data.reviews[].reviewer.display_name` | `string` | Yes | Example: `Jane S`. |
| `data.reviews[].reviewer.avatar_url` | `string` | No | Example: `https://example.com/reviews/123`. |
| `data.reviews[].media` | `object[]` | Yes |  |
| `data.reviews[].media[].type` | `enum<string>` | Yes | Whether the item is an image or a video. Allowed values: `IMAGE`, `VIDEO`. Example: `IMAGE`. |
| `data.reviews[].media[].url` | `string` | Yes | Delivery URL for the full asset. Example: `https://example.com/reviews/123`. |
| `data.reviews[].media[].thumbnail_url` | `string` | Yes | Delivery URL for the thumbnail. Example: `https://example.com/reviews/123`. |
| `data.reviews[].media[].aspect_ratio` | `number` | Yes | Width divided by height. Example: `1.5`. |
| `data.reviews[].media[].aspect_ratio_type` | `enum<string>` | Yes | Coarse orientation bucket. Allowed values: `LANDSCAPE`, `PORTRAIT`, `SQUARE`. Example: `LANDSCAPE`. |
| `data.reviews[].media[].video_length` | `number` | No | Video duration in seconds. Example: `12`. |
| `data.reviews[].media[].id` | `string` | Yes | Stable opaque asset identity shared by detail and preview. Example: `rmed_abc123`. |
| `data.reviews[].media[].alt` | `string` | No | Available image alternative text, localized with source fallback. Example: `Blue shirt worn outdoors`. |
| `data.reviews[].media[].thumbnail_alt` | `string` | No | Available video poster alternative text. Example: `Blue shirt video preview`. |
| `data.reviews[].media[].video_description` | `string` | No | Available description of video imagery. Example: `A shopper shows the fit of a blue shirt.`. |
| `data.reviews[].media[].video_captions` | `string` | No | Available WebVTT captions with cue timing, localized with source fallback. Example: `WEBVTT

00:00.000 --> 00:01.000
The fit is comfortable.`. |
| `data.reviews[].answers` | `object[]` | Yes |  |
| `data.reviews[].answers[].type` | `enum<string>` | Yes | Allowed values: `PRODUCT_ATTRIBUTE`, `COMPANY_ATTRIBUTE`, `PROFILE_PROPERTY`. Example: `PRODUCT_ATTRIBUTE`. |
| `data.reviews[].answers[].prompt` | `string` | Yes | Example: `How does it fit?`. |
| `data.reviews[].answers[].format` | `enum<string>` | Yes | Allowed values: `SCALE`, `CENTERED_RANGE`, `SINGLE_SELECT`, `MULTI_SELECT`, `WOULD_RECOMMEND`, `NUMERIC`. Example: `SCALE`. |
| `data.reviews[].answers[].scale` | `object` | No | Current scale size and labels, localized for the selected market. Not a submission-time snapshot. |
| `data.reviews[].answers[].scale.steps` | `number` | Yes | Example: `5`. |
| `data.reviews[].answers[].scale.start_label` | `string` | Yes | Example: `Too small`. |
| `data.reviews[].answers[].scale.end_label` | `string` | Yes | Example: `Too large`. |
| `data.reviews[].answers[].scale.center_label` | `string` | No | Example: `Just right`. |
| `data.reviews[].answers[].scale.step_labels` | `string[]` | No |  |
| `data.reviews[].answers[].options` | `object[]` | No | Current selection options. Labels are localized; values remain stable selection keys. |
| `data.reviews[].answers[].options[].value` | `string` | Yes | Example: `blue`. |
| `data.reviews[].answers[].options[].label` | `string` | Yes | Example: `Blue`. |
| `data.reviews[].answers[].numeric` | `object` | No |  |
| `data.reviews[].answers[].numeric.unit` | `string` | No | Example: `cm`. |
| `data.reviews[].answers[].question_id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rvq_abc123`. |
| `data.reviews[].answers[].value` | `number \| string \| boolean \| string[]` | Yes | Example: `4`. |
| `data.reviews[].responses` | `object[]` | Yes |  |
| `data.reviews[].responses[].id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rrsp_abc123`. |
| `data.reviews[].responses[].body` | `string` | Yes | Example: `Comfortable and true to size.`. |
| `data.reviews[].responses[].created_at` | `string<date-time>` | Yes | Example: `2026-09-01T10:00:00.000Z`. |
| `data.reviews[].responses[].updated_at` | `string<date-time>` | No | Example: `2026-09-01T10:00:00.000Z`. |
| `data.reviews[].responses[].author` | `object` | Yes |  |
| `data.reviews[].responses[].author.display_name` | `string` | Yes | Example: `Jane S`. |
| `data.reviews[].helpful` | `object` | No |  |
| `data.reviews[].helpful.up_count` | `number` | Yes | Example: `3`. |
| `data.reviews[].helpful.down_count` | `number` | No | Example: `0`. |
| `data.reviews[].product` | `object` | No |  |
| `data.reviews[].product.id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rev_abc123`. |
| `data.reviews[].product.title` | `string` | No | Example: `Comfortable everyday fit`. |
| `data.reviews[].product.url` | `string` | No | Example: `https://example.com/reviews/123`. |
| `data.reviews[].product.image_url` | `string` | No | Example: `https://example.com/reviews/123`. |
| `data.reviews[].variant` | `object` | No |  |
| `data.reviews[].variant.id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rev_abc123`. |
| `data.reviews[].variant.title` | `string` | No | Example: `Comfortable everyday fit`. |
| `data.reviews[].is_pinned` | `boolean` | Yes | Example: `false`. |
| `data.matched_count` | `number` | Yes | Total reviews matching the active filters, across all pages. Overall ratings are available from the separate summary endpoint. Example: `1`. |
| `data.product` | `object` | Yes |  |
| `data.product.id` | `string` | Yes | Opaque Cevoid identifier. Length: `1 to 200`. Example: `rev_abc123`. |
| `data.product.title` | `string` | No | Example: `Comfortable everyday fit`. |
| `data.product.url` | `string` | No | Example: `https://example.com/reviews/123`. |
| `data.product.image_url` | `string` | No | Example: `https://example.com/reviews/123`. |
| `has_more` | `boolean` | Yes | Example: `false`. |
| `next_cursor` | `string \| null` | Yes | Example: `eyJ2IjoxLCJjdXJzb3IiOiJyZXZfYWJjMTIzIn0`. |
| `included` | `object` | No |  |
| `included.localization` | `object` | Yes |  |
| `included.localization.product_reviews` | `object[]` | No |  |
| `included.localization.product_reviews[].id` | `string` | Yes | Public ID matching the returned entity or nested item. Length: `1 to 200`. Example: `rev_abc123`. |
| `included.localization.product_reviews[].has_translations` | `boolean` | Yes | True when the returned translatable content differs from its source representation. Does not mean every field was translated. Example: `true`. |
| `included.localization.product_reviews[].original` | `object \| null` | Yes | Complete source projection of translatable fields, with the same visibility and masking rules. Null when no returned content differs. Not edit history. |
| `included.localization.product_reviews[].original.title` | `string` | No | Reviewer-supplied headline. In localized responses, title and body both use source text unless a complete translation is available. Example: `Comfortable everyday fit`. |
| `included.localization.product_reviews[].original.body` | `string` | No | Reviewer-supplied text. Absent for star-only reviews. In localized responses, title and body both use source text unless a complete translation is available. Example: `Comfortable and true to size.`. |
| `included.localization.review_responses` | `object[]` | No |  |
| `included.localization.review_responses[].id` | `string` | Yes | Public ID matching the returned entity or nested item. Length: `1 to 200`. Example: `rev_abc123`. |
| `included.localization.review_responses[].has_translations` | `boolean` | Yes | True when the returned translatable content differs from its source representation. Does not mean every field was translated. Example: `true`. |
| `included.localization.review_responses[].original` | `object \| null` | Yes | Complete source projection of translatable fields, with the same visibility and masking rules. Null when no returned content differs. Not edit history. |
| `included.localization.review_responses[].original.body` | `string` | Yes | Reply text as published. Example: `Comfortable and true to size.`. |
| `included.localization.review_media` | `object[]` | No |  |
| `included.localization.review_media[].id` | `string` | Yes | Public ID matching the returned entity or nested item. Length: `1 to 200`. Example: `rev_abc123`. |
| `included.localization.review_media[].has_translations` | `boolean` | Yes | True when the returned translatable content differs from its source representation. Does not mean every field was translated. Example: `true`. |
| `included.localization.review_media[].original` | `object \| null` | Yes | Complete source projection of translatable fields, with the same visibility and masking rules. Null when no returned content differs. Not edit history. |
| `included.localization.review_media[].original.alt` | `string` | No | Available image alternative text, localized with source fallback. Example: `Blue shirt worn outdoors`. |
| `included.localization.review_media[].original.thumbnail_alt` | `string` | No | Available video poster alternative text. Example: `Blue shirt video preview`. |
| `included.localization.review_media[].original.video_description` | `string` | No | Available description of video imagery. Example: `A shopper shows the fit of a blue shirt.`. |
| `included.localization.review_media[].original.video_captions` | `string` | No | Available WebVTT captions with cue timing, localized with source fallback. Example: `WEBVTT

00:00.000 --> 00:01.000
The fit is comfortable.`. |
| `included.localization.questions` | `object[]` | No |  |
| `included.localization.questions[].id` | `string` | Yes | Public ID matching the returned entity or nested item. Length: `1 to 200`. Example: `rev_abc123`. |
| `included.localization.questions[].has_translations` | `boolean` | Yes | True when the returned translatable content differs from its source representation. Does not mean every field was translated. Example: `true`. |
| `included.localization.questions[].original` | `object \| null` | Yes | Complete source projection of translatable fields, with the same visibility and masking rules. Null when no returned content differs. Not edit history. |
| `included.localization.questions[].original.prompt` | `string` | Yes | Example: `How does it fit?`. |
| `included.localization.questions[].original.options` | `object[]` | No | Current selection options. Labels are localized; values remain stable selection keys. |
| `included.localization.questions[].original.options[].value` | `string` | Yes | Example: `blue`. |
| `included.localization.questions[].original.options[].label` | `string` | Yes | Example: `Blue`. |
| `included.localization.questions[].original.scale` | `object` | No |  |
| `included.localization.questions[].original.scale.start_label` | `string` | Yes | Example: `Too small`. |
| `included.localization.questions[].original.scale.end_label` | `string` | Yes | Example: `Too large`. |
| `included.localization.questions[].original.scale.center_label` | `string` | No | Example: `Just right`. |
| `included.localization.questions[].original.scale.step_labels` | `string[]` | No |  |

Response headers:

| Header | Type | Description |
| --- | --- | --- |
| `Cevoid-Processing-Ms` | `string` | End-to-end request processing time in milliseconds |
| `Cevoid-Request-Id` | `string` | Request identifier for support and tracing |
| `RateLimit-Limit` | `string` | Current rate limit bucket size |
| `RateLimit-Remaining` | `string` | Remaining requests in the current rate limit window |
| `RateLimit-Reset` | `integer` | Seconds until the next request token is available |

```json
{
  "data": {
    "reviews": [
      {
        "rating": 5,
        "title": "Comfortable everyday fit",
        "body": "Comfortable and true to size.",
        "language": "en",
        "purchase_verification": "UNVERIFIED",
        "is_incentivized": false,
        "is_insider": false,
        "channel": "IMPORT",
        "submitted_at": "2026-09-01T10:00:00.000Z",
        "media": [],
        "answers": [],
        "responses": [],
        "id": "rev_abc123",
        "published_at": "2026-09-01T10:00:00.000Z",
        "reviewer": {
          "display_name": "Jane S"
        },
        "is_pinned": false,
        "product": {
          "id": "prd_abc123",
          "title": "Everyday shirt",
          "url": "https://example.com/products/everyday-shirt"
        },
        "variant": {
          "id": "var_abc123",
          "title": "Blue / Medium"
        }
      }
    ],
    "matched_count": 1,
    "product": {
      "id": "prd_abc123",
      "title": "Everyday shirt",
      "url": "https://example.com/products/everyday-shirt"
    }
  },
  "has_more": false,
  "next_cursor": null
}
```

### 400: Bad request

`application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `additional_data` | `object` | No | Optional machine-readable error details. |
| `additional_data.{key}` | `any \| null` | No |  |
| `status` | `integer` | Yes | HTTP status code. Example: `404`. |
| `code` | `string` | Yes | Stable machine-readable error code. Example: `not_found`. |
| `message` | `string` | Yes | Human-readable error description. Example: `Resource not found`. |
| `request_id` | `string` | No | Request identifier for support and tracing. Example: `req_abc123`. |

Response headers: the same as 200.

```json
{
  "additional_data": {
    "property1": null,
    "property2": null
  },
  "status": 400,
  "code": "bad_request",
  "message": "Bad request",
  "request_id": "req_abc123"
}
```

### 401: Unauthorized

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "status": 401,
  "code": "unauthorized",
  "message": "Unauthorized",
  "request_id": "req_abc123"
}
```

### 403: Forbidden

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "status": 403,
  "code": "forbidden",
  "message": "Forbidden",
  "request_id": "req_abc123"
}
```

### 404: Not found

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "additional_data": {
    "property1": null,
    "property2": null
  },
  "status": 404,
  "code": "not_found",
  "message": "Resource not found",
  "request_id": "req_abc123"
}
```

### 409: Conflict

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "status": 409,
  "code": "conflict",
  "message": "Conflict",
  "request_id": "req_abc123"
}
```

### 422: Validation error

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "additional_data": {
    "property1": null,
    "property2": null
  },
  "status": 422,
  "code": "validation_error",
  "message": "Validation error",
  "request_id": "req_abc123"
}
```

### 429: Too many requests

`application/json`

Same fields as 400.

Response headers:

| Header | Type | Description |
| --- | --- | --- |
| `Cevoid-Processing-Ms` | `string` | End-to-end request processing time in milliseconds |
| `Cevoid-Request-Id` | `string` | Request identifier for support and tracing |
| `RateLimit-Limit` | `string` | Current rate limit bucket size |
| `RateLimit-Remaining` | `string` | Remaining requests in the current rate limit window |
| `RateLimit-Reset` | `integer` | Seconds until the next request token is available |
| `Retry-After` | `integer` | Seconds to wait before retrying the request |

```json
{
  "status": 429,
  "code": "too_many_requests",
  "message": "Too many requests",
  "request_id": "req_abc123"
}
```

### 500: Internal server error

`application/json`

Same fields as 400.

Response headers: the same as 200.

```json
{
  "status": 500,
  "code": "internal_server_error",
  "message": "Internal server error",
  "request_id": "req_abc123"
}
```

## Request example

```bash
curl 'https://api.widget.cevoid.com/v1/products/rev_abc123/reviews' \
  -H "Authorization: Bearer ${CEVOID_PUBLISHABLE_KEY}"
```

---

Canonical: https://docs.cevoid.com/developers/storefront-api/reviews/list-product-reviews
