> ## Documentation Index
> Fetch the complete documentation index at: https://cloro.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a Google shopping card product

> Get the product panel that a Google Search shopping card or a Google AI Mode product opens: merchant offers, specifications, reviews and images.

A [shopping card](/docs/api-reference/endpoint/google/shopping-cards) opens a Google product panel, not a merchant page, and it carries no product URL. The card's `productResultToken` gets you that panel. Google AI Mode shopping cards and inline products have the same token.

<Frame caption="The product panel that opens from a 'More products' card">
  <img src="https://mintcdn.com/cloro/OxQkn97DzLIpM3cL/images/google/search-shopping-card-product.webp?fit=max&auto=format&n=OxQkn97DzLIpM3cL&q=85&s=a4c878e598d47216074aed35cc75b8d0" alt="Google product panel opened from a More products shopping card" width="1400" height="710" data-path="images/google/search-shopping-card-product.webp" />
</Frame>

## Get a token

Send a [Google Search](/docs/api-reference/endpoint/monitor-google) request with `device: "desktop"` (the default). Cards on the first results page carry a token when Google serves them with a panel reference. A [Google AI Mode](/docs/api-reference/endpoint/aimode/product-results) request returns a token on each shopping card and inline product that links to a Google product viewer. The token adds no cost to either request.

```json theme={null}
{
  "title": "ASICS Women's Gel-Nimbus 28",
  "position": 1,
  "productResultToken": "nuzK-QgAV8BqrZdwQJYZopzdoFhpvKr-N2_YDZevwzg",
  "category": "More products",
  "store": "DICK'S Sporting Goods"
}
```

<Warning>
  A token expires **five minutes after the scrape that issued it**. Time in the async queue counts against those five minutes, for the search and for the product request. If your search ran async, its tokens can expire before you read the result. Use the token immediately, or scrape again.
</Warning>

## Get the product

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.cloro.dev/v1/monitor/google/product" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "productResultToken": "nuzK-QgAV8BqrZdwQJYZopzdoFhpvKr-N2_YDZevwzg"
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "result": {
      "productResults": [
        {
          "title": "Apple - Refurbished Excellent - iPhone 17 Pro 256GB 6.3\" 5G Fully Unlocked, Deep - Blue",
          "brand": "Apple",
          "rating": 4.5,
          "reviews": "14K",
          "stores": [
            {
              "name": "Best Buy",
              "link": "https://www.bestbuy.com/product/apple-iphone-17-pro-256gb-6-3-5g-fully-unlocked-deep-blue/J7XLKFSPR8/sku/12337127?ref=212&loc=marketplace",
              "price": { "value": 1080.0, "currency": "USD", "raw": "$1,080.00" },
              "shipping": "Delivery between May 13 – 25 $16.49",
              "condition": "Pre-owned",
              "buyingOptions": ["Pre-owned", "In stock", "Delivery between May 13 – 25 $16.49"]
            }
          ],
          "specifications": {
            "general": { "Screen Size": "6.3 inches", "Built-in Storage": "256 gigabytes" }
          }
        }
      ]
    }
  }
  ```
</CodeGroup>

The request takes only the token. The token keeps the query, locale and location of the request that issued it, so you cannot change them.

It costs 1 credit plus the 2-credit [sync surcharge](/docs/guides/providers#sync-request-surcharge), and only when it succeeds. It uses a concurrency slot. For async, send `taskType: "GOOGLE_PRODUCT"` with `payload: { "productResultToken": "..." }` to [`POST /v1/async/task`](/docs/api-reference/endpoint/create-async-task) or the batch endpoint. Async costs 1 credit.

<Warning>
  If you send the product request async, set a high [`priority`](/docs/guides/making-requests/async#request-prioritization) (for example `10`). The default priority is 1, so the task waits behind your other queued tasks and the token can expire before the task starts.
</Warning>

## Errors

The failure reason is in `error.details.errorType`. No error is charged.

| Status | `errorType` | Cause |
| - | - | - |
| `404` | `productResultTokenUnknown` | cloro did not issue this token. |
| `410` | `productResultTokenExpired` | The token is older than five minutes. Scrape again. |
| `502` | `googleProductUpstreamEmpty` | Google returned no product data. |
| `502` | `googleProductUnparseable` | cloro could not parse Google's response. |
| `502` | `googleProductPayloadLayoutChanged` | Google returned a panel layout that cloro does not support. |

A failed async task has `status: "FAILED"` and the same `errorType` in `response.error.details`.

## Product structure

The response has one entry in `productResults`, with the same shape as the [right-rail product results](/docs/api-reference/endpoint/google/product-results).

Each entry describes one product cluster and its merchant offers.

| Field | Type | Description |
| - | - | - |
| `title` | string | Product cluster title, as Google displayed it |
| `stores` | array | Merchant offers. See [store structure](#store-structure) |
| `aboutTheProduct` | object | `description` (Google's prose blurb) and `features` (bullets, when shown) |
| `specifications` | object | Spec tables grouped by category. See [specifications](#specifications) |
| `images` | array | `mainUrl` and `thumbnailUrl` per image, often identical |

Only `title` is guaranteed (Google Search entries also always carry `stores`) — anything Google's panel omits is left out of the object rather than sent as `null`. Panels can also carry `rating`, `reviews`, `variants`, `relatedProducts` and `userReviews`, emitted under those names when present, though most carry none of them.

### Specifications

A category name mapped to a flat set of spec key/value pairs:

```json theme={null}
{
  "specifications": {
    "general": {
      "Brand": "Sennheiser",
      "Noise cancellation": "Yes"
    }
  }
}
```

<Warning>
  Category names and spec keys arrive in the **response language** — a `country: "MX"` request returns `{"general": {"Marca": "Sennheiser"}}`. Iterate the object; don't read a hard-coded key like `specifications.general.Brand`.
</Warning>

### Store structure

| Field | Type | Description |
| - | - | - |
| `name` | string | Merchant name (e.g. `"Amazon"`) |
| `link` | string | Merchant product URL, passed through from Google verbatim |
| `price` | object | See [price shape](#price-shape). Omitted entirely when the displayed text can't be parsed |
| `installments` | string | Installment terms as displayed (e.g. `"for 24 mo, $0 now, $798.96 total"`) |
| `description` | string | Merchant's listing title, naming the exact SKU sold (e.g. `"iPhone 17 256GB White - Apple"`) — unlike `title`, which names the product overall |
| `buyingOptions` | array | Merchant badges in display order — stock status, rating, delivery promise, returns window. Google varies these per merchant and locale, so they stay an unparsed list of strings |
| `logo` | string | Merchant logo URL |
| `shipping` | string | Shipping cost or promise as displayed |
| `condition` | string | Item condition (e.g. `"New"`, `"Refurbished"`) |
| `rating` | number | Merchant rating |
| `reviews` | string | Merchant review count, abbreviated (e.g. `"384"`, `"2.3k"`) |
| `redirectLink` | string | Google's `/goto` redirect for `link`, when Google served one. See [redirect links](/docs/api-reference/endpoint/google/resolve-goto) |

Expect the first six on most offers and the rest only occasionally. Everything beyond `name` is optional and omitted when absent — Google shows different combinations per merchant, so an "Out of stock" listing may have no price or link at all.

<Warning>
  `link` is not validated against the cluster. Google occasionally serves a merchant URL for an unrelated SKU, so an offer's link can point at a different product than its `description` names. Verify against `description` if link correctness matters.
</Warning>

### Price shape

| Field | Type | Description |
| - | - | - |
| `value` | number | Parsed numeric price; always present when `price` is emitted. On installment offers this is the monthly payment |
| `currency` | string | ISO 4217 code (e.g. `"USD"`, `"BRL"`, `"MXN"`, `"AED"`, `"JPY"`), or the displayed token verbatim when cloro can't map it. See [currency codes](#currency-codes) |
| `raw` | string | Visible price text verbatim (e.g. `"$149.99"`) |

<Warning>
  `currency` here is an **ISO code** (`"USD"`), while [shopping cards](/docs/api-reference/endpoint/aimode/shopping-cards#price-and-oldprice) carry the symbol as displayed (`"$"`) and [inline products](/docs/api-reference/endpoint/aimode/inline-products#price-and-oldprice) carry either, depending on the data Google supplies. Normalize before comparing across surfaces.
</Warning>

### Currency codes

cloro maps the currency token Google displays next to the price to its ISO 4217 code. Multi-character and word tokens are recognized, so `"R$ 5.597,99"` returns `"BRL"`, `"MXN 19,499.00"` returns `"MXN"`, and `"3,399.00 د.إ"` returns `"AED"`. Yen written as `¥`, `￥` or `円` returns `"JPY"`.

A bare `$` doesn't name a market on its own, because Google writes US, Canadian and Australian offers the same way. On [AI Mode](/docs/api-reference/endpoint/aimode/product-results), cloro resolves a bare `$` from the request `country`: `CA` returns `"CAD"`, `AU` returns `"AUD"`, `NZ` returns `"NZD"`, `SG` returns `"SGD"`, `HK` returns `"HKD"`, and any other country returns `"USD"`. On [Google Search](/docs/api-reference/endpoint/google/product-results), a bare `$` always returns `"USD"`.

An explicit token always takes priority over the request country. A `country: "CA"` request that gets an offer displayed as `"R$ 5.597,99"` still returns `"BRL"`.


## OpenAPI

````yaml api-reference/openapi.json POST /v1/monitor/google/product
openapi: 3.1.0
info:
  title: cloro
  description: >-
    API for monitoring AI responses across different providers and regions. One
    request returns the engine's answer as structured JSON: parsed text, cited
    sources, brand entities, shopping results, and ads, geo-targeted by country;
    coverage varies by provider (`GET /v1/countries?model=<provider>` lists it).


    ## Authentication


    Send your API key as `Authorization: Bearer <key>`. Keys are self-serve from
    the [dashboard](https://dashboard.cloro.dev/api-keys) and every account
    starts on a free tier of 500 monthly credits. One key grants every endpoint;
    there are no per-key scopes today.


    ## Versioning and deprecation


    The version is in the URL path. Every endpoint lives under `/v1/`; there is
    no version header or query parameter.


    Within a version, changes are additive: new endpoints, new optional request
    fields, and new response fields can appear at any time. A client must ignore
    response fields it does not recognize. A breaking change gets a new version
    path (`/v2/`) rather than reusing an existing one.


    A deprecated endpoint says so in its own responses, not only in a changelog:
    `Deprecation` (RFC 9745) carries the date the deprecation took effect,
    `Sunset` (RFC 8594) the date the endpoint stops responding, and a `Link`
    header with `rel="deprecation"` points at the replacement. There is a
    minimum of six months between the two dates for any generally available
    endpoint. Nothing is deprecated today.


    ## Rate limits


    Every authenticated response carries `X-RateLimit-Limit` and
    `X-RateLimit-Remaining`, so a client can self-throttle from the response it
    already has. Scrape endpoints (`/v1/monitor/*` except
    `/v1/monitor/google/goto`) also send the `X-Concurrency-*` headers once a
    request passes validation, and the `X-Credits-*` headers on a `200`. Every
    response carries `X-Request-ID` and `X-Latency-Ms`.


    A `429` has one of three codes: `RATE_LIMIT_EXCEEDED` (one-second window,
    clears almost immediately), `CONCURRENCY_LIMIT_EXCEEDED` (scrape endpoints;
    clears when your in-flight jobs finish) or `QUEUE_LIMIT_EXCEEDED` (async
    submission; clears as queued tasks start processing). None carries
    `Retry-After`, and the IETF `RateLimit-*` header names are not sent.


    ## Errors


    Every 4xx and 5xx response uses the `Error` envelope: `{ "success": false,
    "error": { "code", "message", "details", "timestamp" } }`. `error` is always
    an object. Branch on `error.code`, a stable machine-readable identifier;
    `message` is for a human reading a log, and `details` appears only when
    there is extra context. A `503` (`SERVICE_UNAVAILABLE`) is temporary: retry
    after the number of seconds in its `Retry-After` header.
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  version: 1.0.0
  contact:
    name: cloro support
    email: support@cloro.dev
  termsOfService: https://cloro.dev/terms/
servers:
  - url: https://api.cloro.dev
    description: Production server
security:
  - bearerAuth: []
paths:
  /v1/monitor/google/product:
    post:
      summary: Get a Google shopping card product
      description: >-
        Return the product panel that a Google Search shopping card or a Google
        AI Mode product opens: merchant offers, description, specifications,
        reviews, images and related products. Send the `productResultToken` from
        a Google Search `result.shoppingCards` card, or from a Google AI Mode
        `result.shoppingCards` or `result.inlineProducts` entry.


        Costs 1 credit, plus the 2-credit sync surcharge, and only when the
        request succeeds. A token expires five minutes after the scrape that
        issued it. If you send the request async, set a high `priority`, so that
        the token does not expire in the queue.
      operationId: getGoogleProduct
      requestBody:
        description: The token from a Google Search shopping card
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GoogleProductRequest'
        required: true
      responses:
        '200':
          description: The product panel for the token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GoogleProductResponse'
        '400':
          description: Bad Request - Invalid parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthenticationError'
        '403':
          description: Forbidden - Insufficient credits or access denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenError'
        '404':
          description: >-
            Not Found - The token is not recognized (`error.details.errorType`:
            `productResultTokenUnknown`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundError'
        '410':
          description: >-
            Gone - The token has expired (`error.details.errorType`:
            `productResultTokenExpired`). Scrape again for a new token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundError'
        '429':
          description: Too Many Requests
          headers:
            X-Concurrent-Limit:
              description: Maximum number of concurrent requests allowed
              schema:
                type: integer
                example: 10
            X-Concurrent-Current:
              description: Current number of concurrent requests
              schema:
                type: integer
                example: 11
            X-Concurrent-Remaining:
              description: Number of remaining concurrent slots available
              schema:
                type: integer
                example: 0
            X-Credits-Remaining:
              description: Number of credits remaining in user account
              schema:
                type: integer
                example: 4
            X-Credits-Charged:
              description: Number of credits charged for this request
              schema:
                type: integer
                example: 0
            X-Latency-Ms:
              description: >-
                Server-side processing time for this request, in milliseconds.
                Excludes network transit.
              schema:
                type: integer
                example: 12
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConcurrentLimitError'
        '499':
          description: Client Closed Request - Request was canceled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CanceledError'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalError'
        '502':
          description: >-
            Bad Gateway - Google returned no product data, or a layout cloro
            cannot parse
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExternalServiceError'
components:
  schemas:
    GoogleProductRequest:
      required:
        - productResultToken
      type: object
      properties:
        productResultToken:
          description: >-
            The `productResultToken` from a Google Search shopping card, or from
            a Google AI Mode shopping card or inline product. The token carries
            the query, locale and location of the request that issued it, so the
            request takes no other field.
          type: string
          minLength: 1
          maxLength: 512
          example: nuzK-QgAV8BqrZdwQJYZopzdoFhpvKr-N2_YDZevwzg
      additionalProperties: false
    GoogleProductResponse:
      type: object
      required:
        - success
        - result
      properties:
        success:
          type: boolean
          example: true
        result:
          type: object
          required:
            - productResults
          properties:
            productResults:
              type: array
              description: >-
                One entry: the product panel of the card that issued the token.
                Same shape as Google Search and AI Mode `productResults`. Only
                `title` is always present.
              items:
                $ref: '#/components/schemas/ProductResult'
    ValidationError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: VALIDATION_ERROR
            message:
              type: string
              example: Request validation failed
            details:
              type: array
              description: >-
                One entry per invalid field. Sent on request-body validation
                errors.
              items:
                type: object
                properties:
                  field:
                    type: string
                    example: prompt
                  message:
                    type: string
                    example: Prompt cannot be empty
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    AuthenticationError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - MISSING_API_KEY
                - INVALID_API_KEY_FORMAT
                - INVALID_OR_EXPIRED_API_KEY
              example: MISSING_API_KEY
            message:
              type: string
              example: Missing or invalid API key
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    ForbiddenError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - INSUFFICIENT_CREDITS
              example: INSUFFICIENT_CREDITS
            message:
              type: string
              example: Insufficient credits
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    NotFoundError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: RESOURCE_NOT_FOUND
            message:
              type: string
              example: Route not found
            details:
              type: object
              properties:
                id:
                  type: string
                  example: /v1/invalid-endpoint
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    ConcurrentLimitError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: CONCURRENCY_LIMIT_EXCEEDED
            message:
              type: string
              example: Concurrency limit exceeded
            details:
              type: object
              properties:
                limit:
                  type: number
                  example: 10
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    CanceledError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: REQUEST_CANCELED
            message:
              type: string
              example: Request was canceled by client
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    InternalError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: INTERNAL_SERVER_ERROR
            message:
              type: string
              description: >-
                For example `Maximum retries exceeded` when every attempt
                failed, or `Internal server error` for an unexpected failure.
              example: Maximum retries exceeded
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    ExternalServiceError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: EXTERNAL_SERVICE_ERROR
            message:
              type: string
              example: goto resolve request failed
            details:
              type: object
              properties:
                service:
                  type: string
                  example: google
            timestamp:
              type: string
              format: date-time
              example: '2025-01-15T12:00:00.000Z'
    ProductResult:
      type: object
      description: >-
        One product cluster and its merchant offers. Only `title` is guaranteed;
        anything Google's panel lacks is omitted, never sent as `null`.
      required:
        - title
      properties:
        title:
          type: string
          description: >-
            Product cluster title, as Google displayed it. Empty when the page
            has no product heading.
          example: Apple iPhone 17
        stores:
          type: array
          description: >-
            Merchant offers. Always present on Google Search entries; omitted on
            AI Mode when Google lists none.
          items:
            type: object
            required:
              - name
            properties:
              name:
                type: string
                description: Merchant name
                example: Apple
              link:
                type: string
                format: uri
                description: Merchant product URL, passed through from Google verbatim
                example: https://www.apple.com/shop/buy-iphone/iphone-17
              redirectLink:
                type: string
                description: >-
                  Google's `/goto` redirect for this link, in the absolute form
                  `POST /v1/monitor/google/goto` accepts. Present only when
                  Google served the link as a redirect — the sibling link field
                  holds the destination cloro resolved it to.
                example: >-
                  https://www.google.com/goto?url=CAESaAHrOzAVev0pi_wnTxm1qpiZTmMyAllabLr1or10ZCfvJKPk
              price:
                type: object
                description: Offer price. Omitted when the displayed text can't be parsed.
                required:
                  - value
                  - currency
                properties:
                  value:
                    type: number
                    description: >-
                      Parsed numeric price. On installment offers, the monthly
                      payment.
                    example: 799
                  currency:
                    type: string
                    description: ISO 4217 code, or the displayed token when it maps to none
                    example: USD
                  raw:
                    type: string
                    description: Visible price text verbatim
                    example: $799.00
              installments:
                type: string
                description: Installment terms as displayed
              description:
                type: string
                description: Merchant's listing title, naming the exact SKU sold
                example: iPhone 17 256GB White - Apple
              buyingOptions:
                type: array
                description: >-
                  Merchant badges in display order, such as stock status,
                  delivery promise and returns window
                items:
                  type: string
              logo:
                type: string
                format: uri
                description: Merchant logo URL
              shipping:
                type: string
                description: Shipping cost or promise as displayed
              condition:
                type: string
                description: Item condition
                example: New
              rating:
                type: number
                description: Merchant rating
              reviews:
                type: string
                description: Merchant review count, abbreviated
                example: 2.3k
        aboutTheProduct:
          type: object
          description: Google's description of the product
          properties:
            description:
              type: string
              description: Prose blurb
            features:
              type: array
              description: Feature bullets, when shown
              items:
                type: string
        specifications:
          type: object
          description: >-
            Spec tables: each category name maps to key/value pairs. Category
            names and keys arrive in the response language.
          additionalProperties:
            type: object
            additionalProperties:
              type: string
          example:
            general:
              Brand: Apple
              Storage: 256 GB
        images:
          type: array
          items:
            type: object
            required:
              - mainUrl
            properties:
              mainUrl:
                type: string
                format: uri
              thumbnailUrl:
                type: string
                format: uri
              title:
                type: string
        rating:
          type: number
          description: Product rating
        reviews:
          type: string
          description: Product review count
        variants:
          type: array
          description: Product variants Google offers
          items:
            type: object
        userReviews:
          type: array
          description: User reviews
          items:
            type: object
        relatedProducts:
          type: array
          description: Related products
          items:
            type: object
  headers:
    XRateLimitLimit:
      description: Requests allowed in the current window.
      schema:
        type: integer
        example: 1000
    XRateLimitRemaining:
      description: Requests left in the current window.
      schema:
        type: integer
        example: 997
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        cloro API key as a bearer token. One key grants every endpoint in this
        spec; per-key scopes are not available, so a client cannot request a
        narrower permission. Keys are created and revoked in the
        [dashboard](https://dashboard.cloro.dev/api-keys).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.