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

# List products

> GET /v1/products - Retrieve your product catalog

## Endpoint

```
GET https://api.voyantcloud.com/v1/products
```

Retrieve a list of all travel products in your workspace. Products include tours, experiences, hotels, packages, transfers, and more.

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer token (e.g. <code>Authorization: Bearer YOUR\_API\_KEY</code>)
</ParamField>

## Query parameters

<ParamField query="source" type="string">
  Filter by product source. Options: `owned`, `marketplace`. If omitted, returns both owned and marketplace products.
</ParamField>

<ParamField query="status" type="string">
  Filter by product status. Options: `draft`, `active`, `archived`
</ParamField>

<ParamField query="type" type="string">
  Filter by product type. Options: `tour`, `experience`, `hotel`, `package`, `transfer`,
  `car_rental`, `flight`
</ParamField>

<ParamField query="search" type="string">
  Search products by title
</ParamField>

<ParamField query="categoryIds" type="string">
  Comma-separated list of category IDs to filter by
</ParamField>

<ParamField query="tags" type="string">
  Comma-separated list of tags to filter by
</ParamField>

<ParamField query="locale" type="string" default="en">
  Locale for translated content (e.g., `en`, `fr`, `de`)
</ParamField>

<ParamField query="limit" type="integer" default="50">
  Maximum number of products to return (max 100)
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Number of products to skip for pagination
</ParamField>

<ParamField query="sortBy" type="string" default="updatedAt">
  Sort field. Options: `title`, `createdAt`, `updatedAt`
</ParamField>

<ParamField query="sortOrder" type="string" default="desc">
  Sort order. Options: `asc`, `desc`
</ParamField>

## Request example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.voyantcloud.com/v1/products \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://api.voyantcloud.com/v1/products", {
    headers: {
      Authorization: `Bearer ${process.env.VOYANT_API_KEY}`,
    },
  })

  const products = await response.json()
  console.log(products)
  ```

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

  response = requests.get(
    'https://api.voyantcloud.com/v1/products',
    headers={'Authorization': f"Bearer {os.environ['VOYANT_API_KEY']}"}
  )

  products = response.json()
  print(products)
  ```

  ```php PHP theme={null}
  <?php
  $apiKey = getenv('VOYANT_API_KEY');

  $ch = curl_init('https://api.voyantcloud.com/v1/products');
  curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $apiKey"]);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

  $products = json_decode(curl_exec($ch), true);
  curl_close($ch);

  print_r($products);
  ?>
  ```
</CodeGroup>

## Response

<ResponseField name="id" type="string" required>
  Unique product identifier (UUID)
</ResponseField>

<ResponseField name="title" type="string" required>
  Product name/title
</ResponseField>

<ResponseField name="slug" type="string">
  URL-friendly product identifier
</ResponseField>

<ResponseField name="status" type="string" required>
  Product status: `draft`, `active`, `archived`
</ResponseField>

<ResponseField name="type" type="string">
  Product type: `tour`, `experience`, `hotel`, `package`, `transfer`, etc.
</ResponseField>

<ResponseField name="duration_days" type="integer">
  Duration in days for multi-day products
</ResponseField>

<ResponseField name="currency" type="string">
  3-letter ISO currency code (e.g., `EUR`, `USD`)
</ResponseField>

<ResponseField name="location" type="object">
  Product location information

  <Expandable title="Location properties">
    <ResponseField name="city" type="string">
      City name
    </ResponseField>

    <ResponseField name="country" type="string">
      2-letter ISO country code
    </ResponseField>

    <ResponseField name="coordinates" type="object">
      Latitude and longitude coordinates
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="created_at" type="timestamp">
  ISO 8601 timestamp when product was created
</ResponseField>

<ResponseField name="updated_at" type="timestamp">
  ISO 8601 timestamp when product was last updated
</ResponseField>

<ResponseField name="source" type="string" required>
  Product source: `owned` (your workspace's products) or `marketplace` (enabled provider products)
</ResponseField>

<ResponseField name="providerId" type="string">
  Provider ID (only for marketplace products)
</ResponseField>

<ResponseField name="externalProductId" type="string">
  External product ID from the provider (only for marketplace products)
</ResponseField>

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "data": [
      {
        "id": "prod_123abc",
        "title": "Paris City Tour",
        "status": "active",
        "type": "tour",
        "location": {
          "city": "Paris",
          "country": "FR"
        },
        "tags": ["adventure", "walking"],
        "updated_at": "2024-01-15T10:00:00Z",
        "primaryCategoryId": "cat_tours",
        "categoryIds": ["cat_tours"],
        "source": "owned"
      },
      {
        "id": "pprd_789xyz",
        "title": "Barcelona Tapas Tour",
        "status": "active",
        "type": "experience",
        "location": {
          "city": "Barcelona",
          "country": "ES"
        },
        "tags": ["food", "culture"],
        "updated_at": "2024-01-20T14:30:00Z",
        "primaryCategoryId": null,
        "categoryIds": [],
        "source": "marketplace",
        "providerId": "prvd_partner123",
        "externalProductId": "ext-tapas-001"
      }
    ],
    "meta": {
      "total": 25,
      "limit": 50,
      "offset": 0,
      "hasMore": false
    }
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Unauthorized"
  }
  ```

  ```json 500 Server Error theme={null}
  {
    "error": "Failed to fetch products"
  }
  ```
</ResponseExample>

## Filtering examples

### Filter by source

Retrieve only your owned products:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?source=owned" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Retrieve only marketplace products:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?source=marketplace" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Filter by status

Retrieve only active products:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?status=active" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Filter by type

Get all tours:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?type=tour" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Pagination

Retrieve the next 20 products after skipping the first 20:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?limit=20&offset=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Multiple filters

Active tours only:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?status=active&type=tour" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Rate limits

This endpoint is rate-limited to:

* 3,000 requests per minute
* 30 requests per second (burst)

<Tip>Cache product lists when possible to reduce API calls. Products don't change frequently.</Tip>

## Use cases

### Display product catalog

Fetch and display all products in your booking interface:

```javascript theme={null}
async function loadProductCatalog() {
  const response = await fetch("https://api.voyantcloud.com/v1/products?status=active", {
    headers: { Authorization: `Bearer ${process.env.VOYANT_API_KEY}` },
  })

  const products = await response.json()

  return products.map((product) => ({
    id: product.id,
    name: product.title,
    location: `${product.location?.city}, ${product.location?.country}`,
    duration: `${product.duration_days} day${product.duration_days > 1 ? "s" : ""}`,
  }))
}
```

### Search products

Use the `search` parameter to filter products by title:

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?search=paris&status=active" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Filter by category and tags

```bash theme={null}
curl "https://api.voyantcloud.com/v1/products?categoryIds=cat_tours,cat_experiences&tags=adventure,walking" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Next steps

<CardGroup cols={2}>
  <Card title="Get product" icon="box" href="/api-reference/products/get-product">
    Fetch detailed information about a specific product
  </Card>

  <Card title="Pricing options" icon="tag" href="/api-reference/products/pricing-options">
    Get available pricing options for a product
  </Card>

  <Card title="Extensions" icon="puzzle-piece" href="/api-reference/products/extensions">
    Retrieve product add-ons and extensions
  </Card>

  <Card title="Calculate price" icon="calculator" href="/api-reference/pricing/calculate-price">
    Calculate real-time pricing with taxes
  </Card>
</CardGroup>
