---
title: "API Reference"
description: "GlassHome Hub public HTTP API. Versioned widget catalog endpoints for external integrators."
canonical: https://glasshome.app/docs/api-reference
section: "Hub"
updated: 2026-06-09
---
# API Reference

GlassHome Hub exposes a small public HTTP API at `https://glasshome.app` for reading the widget catalog. These endpoints are unauthenticated. The versioned `/api/v1/widgets/*` endpoints are CORS-open; see the CORS note under Conventions for the one exception.

## What's public

Only the endpoints documented below are part of the public API and covered by versioning. Everything under `/api/*` (without `/v1/`) is internal: it powers the Hub UI and the `@glasshome/widget-cli`, requires a session, and may change without notice. Do not build integrations against `/api/*`.

If you need to publish widgets, use the [Widget CLI](/docs/widget-cli). It handles auth and the publish flow for you.

## API versioning and sunset

The `/api/v1/` prefix is the current stable version. When a breaking change to the API is needed, a new prefix (e.g. `/api/v2/`) will be introduced and the old one will be announced for sunset with reasonable notice.

## Conventions

- All requests/responses are JSON.
- Errors: `{ "error": "<human-readable message>" }` with an appropriate HTTP status.
- Timestamps: ISO 8601, UTC.
- Pagination: page-based (`?page=`, `?limit=`).
- CORS: `Access-Control-Allow-Origin: *` on the `/api/v1/widgets/*` endpoints only. The `/api/widgets/{scope}/registry.json` route sits outside the `/v1/` prefix and sends no CORS header, so cross-origin browser fetches against it will fail (server-to-server and same-origin fetches are unaffected).

## `GET /api/v1/widgets/search`

Search the public widget registry.

### Query parameters

| Parameter | Type | Description |
|---|---|---|
| `q` | string | Free-text. Matches `displayName` and `description`. |
| `compatSdk` | string | SDK semver (e.g. `0.5.2`). Must be a valid semver string or the endpoint returns 400. Adds `latestCompatibleVersion` per result. |
| `scope` | string | Restrict to a scope, e.g. `glasshome`. |
| `official` | boolean | `true` limits to Official widgets; `false` limits to non-official. |
| `page` | number | Page number, default `1`. |
| `limit` | number | Page size, default `20`, max `100`. |

Unlisted widgets are excluded from results.

### Response

```json
{
  "widgets": [/* WidgetSummary */],
  "total": 123,
  "page": 1,
  "limit": 20
}
```

### WidgetSummary fields

| Field | Type | Description |
|---|---|---|
| `scope` | string | Publishing scope (e.g. `"glasshome"`) |
| `name` | string | Widget identifier |
| `displayName` | string | Human-readable display name |
| `description` | string or null | Short description |
| `icon` | string or null | Iconify icon identifier |
| `isOfficial` | boolean or null | Whether this is an Official (GlassHome-maintained) widget |
| `downloadCount` | number or null | Cumulative installs |
| `latestVersion` | VersionEntry or null | Latest non-yanked version. `null` if no published version exists. |
| `latestCompatibleVersion` | VersionEntry or null | Only present when `compatSdk` is supplied. Newest version satisfying the requested SDK range, or `null`. |

### VersionEntry fields

| Field | Type | Description |
|---|---|---|
| `scope` | string | Scope (same as parent) |
| `name` | string | Widget name (same as parent) |
| `version` | string | Semver version string |
| `bundleUrl` | string | CDN URL for the JS bundle |
| `bundleSize` | number | Bundle size in bytes (raw, not gzip) |
| `sha256Hash` | string | SHA-256 hex digest of the bundle |
| `publishedAt` | string | ISO 8601 timestamp |
| `yanked` | boolean | Whether this version has been yanked |
| `yankedReason` | string or null | Human-readable reason if yanked |
| `deprecationMessage` | string or null | Warning shown on widgets using this version |
| `releaseNotes` | string or null | User-facing changelog text |
| `displayName` | string | From version manifest, falls back to widget row |
| `description` | string or null | From version manifest, falls back to widget row |
| `icon` | string or null | From version manifest, falls back to widget row |
| `minSize` | `{w, h}` | Minimum grid size |
| `maxSize` | `{w, h}` | Maximum grid size |
| `sdkVersion` | string | SDK version range the bundle was built against |
| `configVersion` | number or null | Config version for migration support |
| `permissions` | string[] | Declared permissions |
| `license` | string or null | License identifier |

### Error responses

| Status | Condition |
|---|---|
| `400` | `compatSdk` is not a valid semver string |

## `GET /api/v1/widgets/{scope}/{name}`

Single widget detail, including the full version list.

### Response

```json
{
  "widget": {
    "scope": "glasshome",
    "name": "my-widget",
    "displayName": "My Widget",
    "description": "...",
    "icon": "mdi:lightbulb",
    "isOfficial": false,
    "downloadCount": 42,
    "createdAt": "2025-01-01T00:00:00.000Z"
  },
  "versions": [/* VersionEntry (see above) */],
  "latestVersion": "1.2.0"
}
```

`versions` is ordered by `publishedAt` descending. `latestVersion` is the version string of the newest non-yanked published version, or `null` if none exists.

### Error responses

| Status | Condition |
|---|---|
| `404` | Widget not found or is unlisted |

## `POST /api/v1/widgets/{scope}/{name}`

Increments the widget's download counter. Used by dashboards on install. No request body, no authentication required.

This is a best-effort counter. It uses a simple `+1` SQL update with no rate-limiting or deduplication in the current implementation. Do not rely on it for precise install analytics.

### Response

```json
{ "success": true }
```

### Error responses

| Status | Condition |
|---|---|
| `404` | Widget not found |

## `GET /api/widgets/{scope}/registry.json`

Static-shaped registry for a single scope. The dashboard uses this for fast bulk fetch on first load. Suitable for edge caching (no `Cache-Control` set by the route itself). No CORS header (see Conventions): fetch it server-to-server, not from a cross-origin browser context.

## Operational endpoints

A few unauthenticated endpoints exist outside the `/v1/` namespace for tooling and status checks: `GET /api/health`, `GET /api/version` (latest Hub release), `GET /api/widgets/cli-version` (minimum supported `widget-cli` version), `GET /api/stats`, and `GET /api/settings`. They are operational, not versioned, and carry no stability guarantee or CORS header. Use them for status/update checks, not as an integration surface.

## Rate limits

Currently unmetered. Be reasonable; aggressive scraping may get rate-limited later with `429` + `Retry-After`.

## Reporting issues

[Bug Reports](/docs/bug-reports). Include the full request (URL + payload) and the response (status + body).