## GET /api/v2/products/shared-hosting

**List shared-hosting products**

List shared-hosting / webhotel plans that can be ordered through `POST /api/v2/orders`. Use `data[].slug` as `items[].productSlug` on a `shared_hosting` order item, set `items[].primaryDomain` to the domain the hosting account should use, and pick one of the returned `billingCycles[].billingCycle` values. Only order products where `available` is true. This is a product catalog endpoint, not the endpoint for existing shared-hosting accounts; use `GET /api/v2/shared-hosting` for provisioned accounts.

### Related Endpoints

- `GET /api/v2/products/vps`: List regular VPS products
- `GET /api/v2/products/domains`: List domain products
- `GET /api/v2/products/domains/{tld}`: Get TLD pricing and registry requirements

### Headers

- `Accept`: application/json
- No authorization header required.

### Parameters

- `limit` (query, integer) [min: 1, max: 100]: Maximum shared-hosting products to return in this page. Defaults to 20 and is capped at 100. Example: `20`
- `cursor` (query, string): Opaque cursor returned as `nextCursor` from the previous page.
- `locale` (query, string): Optional label locale. Supported values are `en` and `sv`; unsupported values fall back to English. Example: `en`
  Allowed values: en, sv

### Request Example

```bash
curl -X GET "https://cloud.hostup.se/api/v2/products/shared-hosting" \
  -H "Accept: application/json"
```

### Response Schema

- `data` (array<object>, required)
- `data[].id` (string, required): Public shared-hosting product ID. Use `slug` for orders; keep `id` for product detail lookups. Example: `shprod_01hxa3b4c5d6e7f8g9h0j1k2m3`
- `data[].slug` (string, required): Canonical value to send as `items[].productSlug` in `POST /api/v2/orders`. Example: `webbhotell-start`
- `data[].tier` (string, required): Short plan tier used for display and sorting. Example: `start`
- `data[].name` (string, required) Example: `Webbhotell Start`
- `data[].resources` (object, required)
- `data[].resources.cpuCores` (number,null, required): CPU core or CPU-time allowance. `null` means unlimited or not published by the upstream catalog. Example: `1`
- `data[].resources.memoryGb` (number,null, required): RAM allowance in GB. `null` means unlimited or not published by the upstream catalog. Example: `1`
- `data[].resources.storageGb` (number,null, required): Disk quota in GB. `null` means unlimited. Example: `20`
- `data[].resources.bandwidthGb` (number,null, required): Traffic quota in GB. `null` means unlimited. Example: `1024`
- `data[].resources.emailAccountLimit` (integer,null, required): Mailbox limit. `null` means unlimited. Example: `10`
- `data[].resources.databaseLimit` (integer,null, required): Database limit. `null` means unlimited. Example: `5`
- `data[].billing` (object, required)
- `data[].billing.amount` (number, required): Amount for the primary/default billing cycle. Example: `99`
- `data[].billing.currencyCode` (string, required) Example: `SEK`
- `data[].billing.billingCycle` (string, required) Example: `annually`
  Allowed values: monthly, annually, free
- `data[].billingCycles` (array<object>, required): Enabled billing cycles. Use one of these `billingCycle` values in the shared-hosting order item.
- `data[].billingCycles[].billingCycle` (string, required) Example: `annually`
  Allowed values: monthly, annually, free
- `data[].billingCycles[].amount` (number, required) Example: `990`
- `data[].billingCycles[].currencyCode` (string, required) Example: `SEK`
- `data[].billingCycles[].setupAmount` (number,null, required) Example: `null`
- `data[].billingCycles[].isPrimary` (boolean, required) Example: `true`
- `data[].availabilityStatus` (string, required): `available` is orderable; `out_of_stock` means do not create an order for that plan. Example: `available`
  Allowed values: available, out_of_stock, hidden
- `data[].available` (boolean, required) Example: `true`
- `data[].reason` (string,null, required): Customer-facing reason when `available` is false. Example: `null`
- `data[].controlPanel` (object, required)
- `data[].controlPanel.type` (string, required) Example: `cpanel`
  Allowed values: cpanel
- `data[].controlPanel.supportsWhm` (boolean, optional): Present only for reseller-style plans that include WHM. Example: `true`
- `hasMore` (boolean, required) Example: `false`
- `nextCursor` (string,null, required) Example: `null`

### Responses

#### 200 - Cursor-paginated shared-hosting product catalog.
```json
{
  "data": [
    {
      "id": "shprod_01hxa3b4c5d6e7f8g9h0j1k2m3",
      "slug": "webbhotell-start",
      "tier": "start",
      "name": "Webbhotell Start",
      "resources": {
        "cpuCores": 1,
        "memoryGb": 1,
        "storageGb": 20,
        "bandwidthGb": 1024,
        "emailAccountLimit": 10,
        "databaseLimit": 5
      },
      "billing": {
        "amount": 990,
        "currencyCode": "SEK",
        "billingCycle": "annually"
      },
      "billingCycles": [
        {
          "billingCycle": "monthly",
          "amount": 99,
          "currencyCode": "SEK",
          "setupAmount": null,
          "isPrimary": false
        },
        {
          "billingCycle": "annually",
          "amount": 990,
          "currencyCode": "SEK",
          "setupAmount": null,
          "isPrimary": true
        }
      ],
      "availabilityStatus": "available",
      "available": true,
      "reason": null,
      "controlPanel": {
        "type": "cpanel"
      }
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### 400 - Invalid request. The response body is an RFC 7807 Problem Details document.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body failed validation.",
  "code": "invalid_request",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z",
  "errors": [
    {
      "pointer": "/items/0/domainName",
      "detail": "`domainName` is required.",
      "code": "invalid_request"
    }
  ]
}
```

#### 404 - Not found. The resource does not exist or is not owned by the caller.
```json
{
  "type": "https://developer.hostup.se/errors/not_found",
  "title": "Not found",
  "status": 404,
  "detail": "The requested resource could not be found.",
  "code": "not_found",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 429 - Rate limited. Retry after the limit resets. 429 responses include `Retry-After` seconds plus `X-RateLimit-*` headers.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limit_exceeded",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests. Retry after the limit resets.",
  "code": "rate_limit_exceeded",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 500 - Internal error. Retry later or contact support if the issue persists.
```json
{
  "type": "https://developer.hostup.se/errors/internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. Retry later or contact support if the issue persists.",
  "code": "internal_error",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```
