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

# Browse Catalogs

> GET /api/hub/catalogs — search and filter the public Marketplace Hub feed.

## Endpoint

```
GET /api/hub/catalogs
```

Returns a paginated list of published catalogs in the Marketplace Hub. This is a public endpoint — no authentication required.

***

## Query parameters

| Parameter  | Type   | Default   | Description                                                            |
| ---------- | ------ | --------- | ---------------------------------------------------------------------- |
| `q`        | string | —         | Search query (matches name, description, tags)                         |
| `category` | string | —         | Filter by category slug (e.g., `apparel`, `electronics`, `home-goods`) |
| `sort`     | string | `popular` | Sort order: `popular`, `recent`, `score`                               |
| `minScore` | number | —         | Minimum average optimization score (0–100)                             |
| `price`    | string | —         | Filter by pricing: `free` or `paid`                                    |
| `limit`    | number | `20`      | Results per page (max: 100)                                            |
| `offset`   | number | `0`       | Pagination offset                                                      |

***

## Response

```json theme={null}
{
  "catalogs": [
    {
      "id": "hub_cat_9x8k2m",
      "name": "Spring 2026 Apparel",
      "description": "500+ SKUs of curated spring apparel from top European brands.",
      "publisher": {
        "workspaceId": "ws_abc",
        "displayName": "EuroFashion Wholesale",
        "verified": true
      },
      "productCount": 512,
      "price": {
        "model": "free"
      },
      "category": "apparel",
      "tags": ["spring", "2026", "european", "fashion"],
      "score": 78,
      "subscribers": 34,
      "publishedAt": "2026-02-01T09:00:00Z",
      "lastUpdatedAt": "2026-03-10T14:30:00Z",
      "version": 5
    }
  ],
  "total": 147,
  "hasMore": true
}
```

### HubCatalog fields

| Field                   | Type      | Description                                     |
| ----------------------- | --------- | ----------------------------------------------- |
| `id`                    | string    | Hub catalog ID                                  |
| `name`                  | string    | Display name                                    |
| `description`           | string    | Short description (max 140 chars in card view)  |
| `publisher.workspaceId` | string    | Publisher's workspace ID                        |
| `publisher.displayName` | string    | Publisher's display name                        |
| `publisher.verified`    | boolean   | Whether the publisher is verified               |
| `productCount`          | number    | Total products in the catalog                   |
| `price.model`           | string    | `"free"` or `"paid"`                            |
| `price.amount`          | number    | Price in USD (only for `"paid"` catalogs)       |
| `price.interval`        | string    | `"one_time"` or `"monthly"` (only for `"paid"`) |
| `category`              | string    | Primary category slug                           |
| `tags`                  | string\[] | Up to 10 tags                                   |
| `score`                 | number    | Average optimization score (0–100)              |
| `subscribers`           | number    | Active subscriber count                         |
| `publishedAt`           | string    | ISO 8601 timestamp of first publication         |
| `lastUpdatedAt`         | string    | ISO 8601 timestamp of last version              |
| `version`               | number    | Current version number                          |

### Pagination fields

| Field     | Type    | Description                                     |
| --------- | ------- | ----------------------------------------------- |
| `total`   | number  | Total matching catalogs (ignoring limit/offset) |
| `hasMore` | boolean | Whether more results are available              |

***

## Examples

<CodeGroup>
  ```bash curl theme={null}
  # Browse all catalogs (default: sorted by popular, 20 per page)
  curl "https://app.alana.shopping/api/hub/catalogs"

  # Search for apparel catalogs with a high score
  curl "https://app.alana.shopping/api/hub/catalogs?q=apparel&category=apparel&sort=score&minScore=70&limit=50"

  # Get free catalogs sorted by most recent
  curl "https://app.alana.shopping/api/hub/catalogs?price=free&sort=recent&limit=20"

  # Paginate: second page
  curl "https://app.alana.shopping/api/hub/catalogs?limit=20&offset=20"
  ```

  ```javascript JavaScript theme={null}
  // Search for electronics catalogs
  const params = new URLSearchParams({
    q: 'electronics',
    category: 'electronics',
    sort: 'score',
    minScore: '65',
    limit: '20',
    offset: '0',
  });

  const response = await fetch(
    `https://app.alana.shopping/api/hub/catalogs?${params}`
  );

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const { catalogs, total, hasMore } = await response.json();

  console.log(`Found ${total} catalogs (${catalogs.length} returned)`);
  catalogs.forEach(cat => {
    console.log(`${cat.name} — ${cat.productCount} products — score: ${cat.score}`);
  });

  // Load more pages
  if (hasMore) {
    const nextParams = new URLSearchParams({ ...Object.fromEntries(params), offset: '20' });
    const nextResponse = await fetch(
      `https://app.alana.shopping/api/hub/catalogs?${nextParams}`
    );
    const nextPage = await nextResponse.json();
  }
  ```

  ```python Python theme={null}
  import requests

  # Search for apparel catalogs sorted by score
  response = requests.get(
      "https://app.alana.shopping/api/hub/catalogs",
      params={
          "q": "apparel",
          "category": "apparel",
          "sort": "score",
          "minScore": 70,
          "limit": 50,
      }
  )

  response.raise_for_status()
  data = response.json()

  print(f"Found {data['total']} matching catalogs")
  for catalog in data["catalogs"]:
      price = catalog["price"]["model"]
      print(f"{catalog['name']} — {catalog['productCount']} products — {price} — score: {catalog['score']}")

  # Paginate through all results
  def get_all_catalogs(**params):
      all_catalogs = []
      offset = 0
      while True:
          response = requests.get(
              "https://app.alana.shopping/api/hub/catalogs",
              params={**params, "limit": 100, "offset": offset}
          )
          data = response.json()
          all_catalogs.extend(data["catalogs"])
          if not data["hasMore"]:
              break
          offset += 100
      return all_catalogs
  ```
</CodeGroup>

***

## Available categories

| Slug            | Display name           |
| --------------- | ---------------------- |
| `apparel`       | Apparel & Fashion      |
| `electronics`   | Electronics & Tech     |
| `home-goods`    | Home & Living          |
| `beauty`        | Beauty & Personal Care |
| `sports`        | Sports & Outdoors      |
| `food-beverage` | Food & Beverage        |
| `office`        | Office & B2B Supplies  |
| `automotive`    | Automotive             |
| `toys`          | Toys & Games           |
| `health`        | Health & Wellness      |

***

## Error responses

| HTTP status | Code                  | Description                                        |
| ----------- | --------------------- | -------------------------------------------------- |
| 400         | `VALIDATION_ERROR`    | Invalid query parameter value                      |
| 429         | `RATE_LIMIT_EXCEEDED` | Slow down — 60 requests/minute for public endpoint |
