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

# Real-Time Catalog Access

> Serve Snappy's catalog live from the V3 API on demand, with optional edge caching.

The Real-Time Catalog Access pattern serves Snappy's V3 catalog API on demand, with data fetched live from Snappy (and optionally cached at your edge). No local mirror, no nightly sync. Snappy is the source of truth.

## When to use

**Use real-time when:**

* You want live pricing and availability without sync lag
* You don't have local catalog infrastructure to maintain
* You want the simplest possible integration path

**Use [Bulk Catalog Export](/patterns/bulk-catalog-export) instead when:**

* You already have a local catalog or search index you want to blend Snappy products into
* You need offline access or want to blend with other data sources
* Your rendering layer can't tolerate API latency on every request

## Two paths

### Collection-based (recommended)

Fetch products scoped to a specific collection — the usual pattern for partners who have a dedicated collection for their program.

```text theme={null}
GET /v3/collections/{collectionId}/products
```

This endpoint uses **cursor pagination**. Follow `links.next` until it returns `null`.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function fetchCollection(collectionId, { location = "US" } = {}) {
    const products = [];
    let cursor = null;
    do {
      const url = new URL(`${SNAPPY}/v3/collections/${collectionId}/products`);
      url.searchParams.set("location", location);      // ISO 3166-1 alpha-2, required
      url.searchParams.set("include", "brand,tags");
      url.searchParams.set("fields", "priceRange,variantsCount");
      url.searchParams.set("page[size]", "100");       // max 300
      if (cursor) url.searchParams.set("page[cursor]", cursor);
      const res = await fetch(url, { headers });
      if (!res.ok) throw new Error(`Snappy ${res.status}: ${await res.text()}`);
      const body = await res.json();
      products.push(...body.data);
      cursor = body.links?.next
        ? new URL(body.links.next).searchParams.get("page[cursor]")
        : null;
    } while (cursor);
    return products;
  }
  ```

  ```python Python theme={null}
  from urllib.parse import urlparse, parse_qs
  import requests

  def fetch_collection(collection_id, location="US"):
      products = []
      cursor = None
      while True:
          params = {
              "location": location,
              "include": "brand,tags",
              "fields": "priceRange,variantsCount",
              "page[size]": 100,
          }
          if cursor:
              params["page[cursor]"] = cursor
          res = requests.get(
              f"{SNAPPY}/v3/collections/{collection_id}/products",
              headers=headers, params=params,
          )
          res.raise_for_status()
          body = res.json()
          products.extend(body["data"])
          next_link = (body.get("links") or {}).get("next")
          if not next_link:
              break
          cursor = parse_qs(urlparse(next_link).query)["page[cursor]"][0]
      return products
  ```
</CodeGroup>

### Filter-based

Fetch from the full catalog with filters, without scoping to a collection.

```text theme={null}
GET /v3/products?filter[collectionId]=...&location=US
```

Useful when you want to merge multiple collections, apply tag or price filters globally, or access the catalog without a fixed `collectionId`.

## After picking a product

Once a user selects a product, fetch the full detail and its variants:

```javascript JavaScript theme={null}
// Product detail (options matrix for variant pickers)
const product = await fetch(`${SNAPPY}/v3/products/${productId}?fields=options,priceRange,variantsCount&include=brand,tags`, { headers }).then(r => r.json());

// Orderable variants
const { data: variants } = await fetch(`${SNAPPY}/v3/products/${productId}/variants?fields=price`, { headers }).then(r => r.json());
```

The product detail endpoint returns the object directly (no `data` wrapper). The variants endpoint returns `{ data, links }` with page-number pagination.

### Resolve the selected variant at checkout

```javascript JavaScript theme={null}
// `selection` maps option name → chosen value, e.g. { color: "black", size: "l" }
function resolveVariant(variants, selection) {
  return variants.find((v) =>
    Object.entries(selection).every(
      ([level, value]) => v.selectedOptions[level] === value
    )
  );
}
const variant = resolveVariant(variants, { color: "black", size: "l" });
// variant.id goes into POST /v3/orders
```

Pre-select each option's `firstSelectableVariant` on page load to avoid dead-end combinations.

## Caching guidance

Fetching live on every render is fine for most traffic levels. Add a short cache at your edge if you need to reduce latency or API call volume:

| Data                                                       | Recommended TTL |
| :--------------------------------------------------------- | :-------------- |
| Product list (collection)                                  | 5–15 minutes    |
| Product detail + variants                                  | 5–15 minutes    |
| Product tags                                               | 1 hour          |
| Availability (`GET /v3/variants/{variantId}/availability`) | 2–5 minutes     |

Cache by `(collectionId, location, page[cursor])` for list pages, and by `(productId, location)` for detail pages.

## Common pitfalls

| Pitfall                                        | Fix                                                                                 |
| :--------------------------------------------- | :---------------------------------------------------------------------------------- |
| Serving stale out-of-stock products            | Cache with short TTL; check `GET /v3/variants/{variantId}/availability` at checkout |
| Missing `location` param                       | Always pass `location` (ISO 3166-1 alpha-2) — it affects availability and pricing   |
| Rendering a product with no selectable variant | Pre-select `firstSelectableVariant`; never show a picker with no valid default      |
| Exceeding rate limits on page load             | Cache aggressively, batch requests, use `page[size]=300` to minimize pages          |

## Related

* [Bulk Catalog Export](/patterns/bulk-catalog-export) — for local catalog infrastructure
* [Place Orders](/patterns/place-orders) — the next step after catalog browsing
* [Build a Rewards Experience](/pages/marketplace) — end-to-end recipe using this pattern
