---
title: "List your sites"
method: GET
path: "/api/v1/sites"
tags: ["Sites"]
---

# List your sites

`GET /api/v1/sites`

## Request

- Base URL: `https://api.dotsy.ai`
- URL: `https://api.dotsy.ai/api/v1/sites`
- Auth: HTTP bearer

Every site you can open: the ones in your workspace, plus sites other workspaces shared with you (`shared: true`, with your `role`). Without `limit` you get the whole list in one response, newest-updated first, and no `nextCursor`. With `limit` (1–1000) the list is ordered by `slug` instead, so a page boundary cannot move while you read, and `nextCursor` is the cursor for the next page (null on the last). Send the same `limit`, `q` and `scope` with every `cursor`. A page of up to 100 sites costs the same however many sites you have.

Each site carries its stable `id`; use it, or the slug, in the `{slug}` path of every site operation.

## Query parameters

- `limit` integer
- `cursor` string
- `q` string
- `scope` 'owned' | 'shared'

## Headers

- `X-Dotsy-Workspace` string

## Response `200`

The sites.

- SiteList
  - `sites` SiteSummary[], required
    - `id` string: Stable site id (UUID). Unlike the slug it never changes, and it can stand in for the slug in site URLs (the API answers with a 307 to the canonical slug path). Absent only on a list cached before the field existed; it is back within a minute.
    - `slug` string, required
    - `title` string, nullable, required
    - `updatedAt` number, required: Last change to the site, Unix ms.
    - `publishedAt` number, nullable, required: Last publish time, Unix ms. Null until the first publish.
    - `hasPendingChanges` boolean, required: True when the draft has changes that are not published yet.
    - `shared` boolean, required: True when the site belongs to another workspace and was shared with you. False when you own it.
    - `role` 'owner' | 'editor' | 'viewer': Your role on the site. Present only when `shared` is true.
    - `workspaceName` string: Name of the workspace that owns the site. Present only on shared sites, and only when the list is paged with a `limit` of 100 or less.
    - `publicUrl` string, required: Canonical public site URL for this slug.
    - `liveUrl` string, required: Alias of publicUrl.
    - `liveReady` boolean, required: True after the first publish. Until then the public URL answers 404.
    - `cardPreviewLiveAt` number, nullable: Unix ms when the workspace card live homepage preview was captured. Drives cache-busting on GET …/card-preview?arm=live.
    - `cardPreviewDraftAt` number, nullable: Unix ms when the workspace card draft homepage preview was captured. GET …/card-preview?arm=draft.
  - `nextCursor` string, nullable: Only present when `limit` was sent. Pass it as `cursor` (with the same `limit`) for the next page; null on the last page.

## Other responses

- `400`: `cursor` is not one this API handed out, or was handed out for a different `q` or `scope` (`invalid_cursor`). Restart the listing without a cursor.
- `401`: Missing or invalid credentials.
- `403`: You are not a member of the workspace named in `X-Dotsy-Workspace` (`forbidden_workspace`).

---

[API](https://skmtc.dev/dotsy/apis/dotsy-api.md) · [All operations](https://skmtc.dev/dotsy/apis/dotsy-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dotsy/apis/dotsy-api/revisions/4272ed3b85d7?raw)
