# Search the catalog (https://developer.godaddy.com/en/docs/api-users/shopping/guides/search-catalog)

---
title: Search the catalog
description: >-
  Search for GoDaddy products, look up variants by ID, and retrieve product
  detail using the Shopping API.
keywords: >-
  catalog search, product lookup, POST /catalog/search, POST /catalog/lookup,
  POST /catalog/product, product variants, hosting search
agentNotes:
  permissions:
    - Shopping Catalog
  scopes:
    - 'shopping.catalog:read'
  rateLimit: >-
    Rate-limited per credential. Go to /docs/api-users/rate-limits for current
    values.
  idempotent: true
  destructive: false
  failureRecovery: >-
    All catalog operations are safe to retry. Use a new Request-Id on each
    retry.
related:
  guides:
    - title: About the Shopping API
      href: /docs/api-users/shopping
    - title: Create a checkout and place an order
      href: /docs/api-users/shopping/guides/purchase
  concepts:
    - title: Handle errors
      href: /docs/api-users/errors
    - title: Rate limits
      href: /docs/api-users/rate-limits
  apis:
    - title: Shopping Catalog reference
      href: /docs/references/rest/shopping/search-catalog
---

## Overview

Use the Shopping API v1 Catalog endpoints to browse products, resolve known IDs, and retrieve product detail before checkout.

All catalog operations use `shopping.catalog:read` scope.

Go to [About the Shopping API](https://developer.godaddy.com/docs/api-users/shopping) for required headers and the error model.

## Prerequisites

The following prerequisites are required before you can search the catalog:

* a PAT with `shopping.catalog:read` scope
* `jq` and `uuidgen` for the curl examples

## Authenticate

These examples use a `$GODADDY_PAT` with `shopping.catalog:read` scope.

Go to [About the Shopping API](https://developer.godaddy.com/docs/api-users/shopping#authentication) for the full list of required scopes.

## Search products

`POST /v1/shopping/catalog/search` returns products matching a free-text query or filters. Use this endpoint to show buyers a browsable product list.

The following procedure searches the product catalog.

* Search for available products:

  ```bash tab="curl"
  curl --fail-with-body --silent --show-error -X POST "https://api.godaddy.com/v1/shopping/catalog/search" \
    -H "Authorization: Bearer $GODADDY_PAT" \
    -H "Request-Id: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "context": { "address_country": "US", "currency": "USD", "language": "en" },
      "pagination": { "limit": 20 }
    }'
  ```

  ```js tab="Node"
  const res = await fetch("https://api.godaddy.com/v1/shopping/catalog/search", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GODADDY_PAT}`,
      "Request-Id": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      context: { address_country: "US", currency: "USD", language: "en" },
      pagination: { limit: 20 },
    }),
  });
  const data = await res.json();
  if (!res.ok || data.ucp?.status === "error") throw new Error(data.messages?.[0]?.code ?? `HTTP ${res.status}`);
  const { products, pagination } = data;
  ```

  ```python tab="Python"
  import os, uuid, requests

  res = requests.post(
      "https://api.godaddy.com/v1/shopping/catalog/search",
      headers={
          "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
          "Request-Id": str(uuid.uuid4()),
      },
      json={
          "context": {"address_country": "US", "currency": "USD", "language": "en"},
          "pagination": {"limit": 20},
      },
  )
  data = res.json()
  if not res.ok or data.get("ucp", {}).get("status") == "error":
      raise RuntimeError(data.get("messages", [{}])[0].get("code", f"HTTP {res.status_code}"))
  products = data.get("products", [])
  ```

  ```go tab="Go"
  package main

  import (
      "bytes"
      "crypto/rand"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
  )

  func requestID() string {
      value := make([]byte, 16)
      if _, err := rand.Read(value); err != nil { log.Fatal(err) }
      value[6], value[8] = (value[6]&0x0f)|0x40, (value[8]&0x3f)|0x80
      return fmt.Sprintf("%x-%x-%x-%x-%x", value[:4], value[4:6], value[6:8], value[8:10], value[10:])
  }

  func main() {
      body, _ := json.Marshal(map[string]any{
          "context":    map[string]string{"address_country": "US", "currency": "USD", "language": "en"},
          "pagination": map[string]int{"limit": 20},
      })
      req, _ := http.NewRequest("POST", "https://api.godaddy.com/v1/shopping/catalog/search", bytes.NewReader(body))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
      req.Header.Set("Request-Id", requestID())
      req.Header.Set("Content-Type", "application/json")

      res, err := http.DefaultClient.Do(req)
      if err != nil { log.Fatal(err) }
      defer res.Body.Close()
      if res.StatusCode != http.StatusOK { log.Fatalf("catalog search returned %s", res.Status) }

      var data map[string]any
      json.NewDecoder(res.Body).Decode(&data)
      fmt.Println(data["products"])
  }
  ```

* Search for hosting products by keyword:

  ```bash tab="curl"
  curl --fail-with-body --silent --show-error -X POST "https://api.godaddy.com/v1/shopping/catalog/search" \
    -H "Authorization: Bearer $GODADDY_PAT" \
    -H "Request-Id: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "hosting",
      "context": { "address_country": "US", "currency": "USD", "language": "en" },
      "pagination": { "limit": 20 }
    }'
  ```

  ```js tab="Node"
  const res = await fetch("https://api.godaddy.com/v1/shopping/catalog/search", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GODADDY_PAT}`,
      "Request-Id": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      query: "hosting",
      context: { address_country: "US", currency: "USD", language: "en" },
      pagination: { limit: 20 },
    }),
  });
  const data = await res.json();
  if (!res.ok || data.ucp?.status === "error") throw new Error(data.messages?.[0]?.code ?? `HTTP ${res.status}`);
  const { products, pagination } = data;
  ```

  ```python tab="Python"
  import os, uuid, requests

  res = requests.post(
      "https://api.godaddy.com/v1/shopping/catalog/search",
      headers={
          "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
          "Request-Id": str(uuid.uuid4()),
      },
      json={
          "query": "hosting",
          "context": {"address_country": "US", "currency": "USD", "language": "en"},
          "pagination": {"limit": 20},
      },
  )
  data = res.json()
  if not res.ok or data.get("ucp", {}).get("status") == "error":
      raise RuntimeError(data.get("messages", [{}])[0].get("code", f"HTTP {res.status_code}"))
  products = data.get("products", [])
  ```

  ```go tab="Go"
  package main

  import (
      "bytes"
      "crypto/rand"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
  )

  func requestID() string {
      value := make([]byte, 16)
      if _, err := rand.Read(value); err != nil { log.Fatal(err) }
      value[6], value[8] = (value[6]&0x0f)|0x40, (value[8]&0x3f)|0x80
      return fmt.Sprintf("%x-%x-%x-%x-%x", value[:4], value[4:6], value[6:8], value[8:10], value[10:])
  }

  func main() {
      body, _ := json.Marshal(map[string]any{
          "query":      "hosting",
          "context":    map[string]string{"address_country": "US", "currency": "USD", "language": "en"},
          "pagination": map[string]int{"limit": 20},
      })
      req, _ := http.NewRequest("POST", "https://api.godaddy.com/v1/shopping/catalog/search", bytes.NewReader(body))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
      req.Header.Set("Request-Id", requestID())
      req.Header.Set("Content-Type", "application/json")

      res, err := http.DefaultClient.Do(req)
      if err != nil { log.Fatal(err) }
      defer res.Body.Close()
      if res.StatusCode != http.StatusOK { log.Fatalf("catalog search returned %s", res.Status) }

      var data map[string]any
      json.NewDecoder(res.Body).Decode(&data)
      fmt.Println(data["products"])
  }
  ```

Use `pagination.cursor` from the response and pass it in `pagination.cursor` on the next request to page through results when `has_next_page` is `true`.

## Look up products by ID

`POST /v1/shopping/catalog/lookup` fetches one or more specific variants by their IDs. Use this when you already know the variant ID and need current pricing and availability.

The following procedure looks up variants by ID.

* Look up specific variants:

  ```bash tab="curl"
  curl --fail-with-body --silent --show-error -X POST "https://api.godaddy.com/v1/shopping/catalog/lookup" \
    -H "Authorization: Bearer $GODADDY_PAT" \
    -H "Request-Id: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg id "$VARIANT_ID" '{"ids":[$id],"context":{"address_country":"US","currency":"USD","language":"en"}}')"
  ```

  ```js tab="Node"
  const res = await fetch("https://api.godaddy.com/v1/shopping/catalog/lookup", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GODADDY_PAT}`,
      "Request-Id": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      ids: [process.env.VARIANT_ID],
      context: { address_country: "US", currency: "USD", language: "en" },
    }),
  });
  const data = await res.json();
  if (!res.ok || data.ucp?.status === "error") throw new Error(data.messages?.[0]?.code ?? `HTTP ${res.status}`);
  const { products, messages } = data;
  ```

  ```python tab="Python"
  import os, uuid, requests

  res = requests.post(
      "https://api.godaddy.com/v1/shopping/catalog/lookup",
      headers={
          "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
          "Request-Id": str(uuid.uuid4()),
      },
      json={
          "ids": [os.environ["VARIANT_ID"]],
          "context": {"address_country": "US", "currency": "USD", "language": "en"},
      },
  )
  data = res.json()
  if not res.ok or data.get("ucp", {}).get("status") == "error":
      raise RuntimeError(data.get("messages", [{}])[0].get("code", f"HTTP {res.status_code}"))
  products = data.get("products", [])
  ```

  ```go tab="Go"
  package main

  import (
      "bytes"
      "crypto/rand"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
  )

  func requestID() string {
      value := make([]byte, 16)
      if _, err := rand.Read(value); err != nil { log.Fatal(err) }
      value[6], value[8] = (value[6]&0x0f)|0x40, (value[8]&0x3f)|0x80
      return fmt.Sprintf("%x-%x-%x-%x-%x", value[:4], value[4:6], value[6:8], value[8:10], value[10:])
  }

  func main() {
      body, _ := json.Marshal(map[string]any{
          "ids":     []string{os.Getenv("VARIANT_ID")},
          "context": map[string]string{"address_country": "US", "currency": "USD", "language": "en"},
      })
      req, _ := http.NewRequest("POST", "https://api.godaddy.com/v1/shopping/catalog/lookup", bytes.NewReader(body))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
      req.Header.Set("Request-Id", requestID())
      req.Header.Set("Content-Type", "application/json")

      res, err := http.DefaultClient.Do(req)
      if err != nil { log.Fatal(err) }
      defer res.Body.Close()
      if res.StatusCode != http.StatusOK { log.Fatalf("catalog lookup returned %s", res.Status) }

      var data map[string]any
      json.NewDecoder(res.Body).Decode(&data)
      fmt.Println(data["products"])
  }
  ```

Each returned variant includes an `inputs[]` array showing how the variant matched the requested ID — either `exact` or `featured`.

## Get product detail

`POST /v1/shopping/catalog/product` retrieves one product and its available variants. The current catalog does not use option-selection narrowing, so send only `id` and optional context.

The following procedure retrieves product detail.

* Get detail for a specific product:

  ```bash tab="curl"
  curl --fail-with-body --silent --show-error -X POST "https://api.godaddy.com/v1/shopping/catalog/product" \
    -H "Authorization: Bearer $GODADDY_PAT" \
    -H "Request-Id: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg id "$VARIANT_ID" '{"id":$id,"context":{"address_country":"US","currency":"USD","language":"en"}}')"
  ```

  ```js tab="Node"
  const res = await fetch("https://api.godaddy.com/v1/shopping/catalog/product", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GODADDY_PAT}`,
      "Request-Id": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      id: process.env.VARIANT_ID,
      context: { address_country: "US", currency: "USD", language: "en" },
    }),
  });
  const data = await res.json();
  if (!res.ok || data.ucp?.status === "error") throw new Error(data.messages?.[0]?.code ?? `HTTP ${res.status}`);
  const { product } = data;
  ```

  ```python tab="Python"
  import os, uuid, requests

  res = requests.post(
      "https://api.godaddy.com/v1/shopping/catalog/product",
      headers={
          "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
          "Request-Id": str(uuid.uuid4()),
      },
      json={
          "id": os.environ["VARIANT_ID"],
          "context": {"address_country": "US", "currency": "USD", "language": "en"},
      },
  )
  data = res.json()
  if not res.ok or data.get("ucp", {}).get("status") == "error":
      raise RuntimeError(data.get("messages", [{}])[0].get("code", f"HTTP {res.status_code}"))
  product = data.get("product")
  ```

  ```go tab="Go"
  package main

  import (
      "bytes"
      "crypto/rand"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
  )

  func requestID() string {
      value := make([]byte, 16)
      if _, err := rand.Read(value); err != nil { log.Fatal(err) }
      value[6], value[8] = (value[6]&0x0f)|0x40, (value[8]&0x3f)|0x80
      return fmt.Sprintf("%x-%x-%x-%x-%x", value[:4], value[4:6], value[6:8], value[8:10], value[10:])
  }

  func main() {
      body, _ := json.Marshal(map[string]any{
          "id":      os.Getenv("VARIANT_ID"),
          "context": map[string]string{"address_country": "US", "currency": "USD", "language": "en"},
      })
      req, _ := http.NewRequest("POST", "https://api.godaddy.com/v1/shopping/catalog/product", bytes.NewReader(body))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
      req.Header.Set("Request-Id", requestID())
      req.Header.Set("Content-Type", "application/json")

      res, err := http.DefaultClient.Do(req)
      if err != nil { log.Fatal(err) }
      defer res.Body.Close()
      if res.StatusCode != http.StatusOK { log.Fatalf("product lookup returned %s", res.Status) }

      var data map[string]any
      json.NewDecoder(res.Body).Decode(&data)
      fmt.Println(data["product"])
  }
  ```

Product detail responses might include `required_agreements[]`. The resolved checkout is authoritative. Go to [Create a checkout and place an order](https://developer.godaddy.com/docs/api-users/shopping/guides/purchase#handle-required-agreements) for the consent flow.

## Interpret catalog results

Catalog amounts use ISO 4217 minor units. For USD, an amount of `999` represents `$9.99`.

| Field                      | Use                                                                                 |
| -------------------------- | ----------------------------------------------------------------------------------- |
| `products[].id`            | Identifies the product family. Do not assume this ID is purchasable.                |
| `products[].variants[].id` | Identifies a purchasable option to send as `line_items[].item.id`.                  |
| `price` and `list_price`   | Show the current selling price and optional comparison price.                       |
| `renewal_price`            | Shows an optional renewal amount for recurring products.                            |
| `availability.available`   | Indicates whether the variant can be purchased.                                     |
| `inputs[]`                 | Correlates lookup results with requested IDs.                                       |
| `input_schema`             | Defines product-specific configuration to send as `line_items[].input`.             |
| `included_products[]`      | Describes products bundled into a catalog offer.                                    |
| `required_agreements[]`    | Provides early disclosure; the checkout's resolved agreements remain authoritative. |

## Verify the result

Confirm that the selected variant has `availability.available: true`, a price in the expected currency, and any required product input.

Save the variant ID for checkout. Go to [Create a checkout and place an order](https://developer.godaddy.com/docs/api-users/shopping/guides/purchase) to purchase it.

## Example response

The search endpoint returns a `products[]` array. Each product contains a `variants[]` array. Use `variants[].id` to create a checkout session:

```json
{
  "products": [
    {
      "id": "hosting-economyapi",
      "title": "Economy Hosting",
      "variants": [
        {
          "id": "hosting-economyapi:1mo",
          "title": "Economy 1 Month",
          "price": { "amount": 999, "currency": "USD" },
          "availability": { "available": true }
        }
      ]
    }
  ],
  "pagination": { "has_next_page": false }
}
```

## Common errors

| Response                | `messages[].code`      | Cause                                              | Action                                                           |
| ----------------------- | ---------------------- | -------------------------------------------------- | ---------------------------------------------------------------- |
| `200` with info message | `not_found`            | A batch lookup could not resolve one requested ID. | Use the returned matches and inspect each informational message. |
| `400`                   | `invalid_request`      | A required request field is missing.               | Correct the request body.                                        |
| `404`                   | `product_not_found`    | Product detail could not resolve the requested ID. | Verify the ID from a catalog search.                             |
| `422`                   | `unsupported_currency` | The requested currency is not supported.           | Use a supported catalog currency.                                |
| `401`                   | —                      | The PAT is missing, invalid, or expired.           | Generate or export a valid PAT.                                  |
| `403`                   | —                      | The PAT lacks `shopping.catalog:read`.             | Generate a PAT with the required scope.                          |
| `429`                   | —                      | Rate limit exceeded.                               | Wait for the `Retry-After` header value, then retry.             |

Batch lookup can return HTTP `200` with matches and informational `not_found` messages. Successful responses can omit `ucp.status`. Go to [About the Shopping API](https://developer.godaddy.com/docs/api-users/shopping#error-responses) for error handling.
