---
title: "Create a site and get its install snippet"
method: POST
path: "/sites"
tags: ["Sites"]
---

# Create a site and get its install snippet

`POST /sites`

## Request

- Base URL: `https://statable.com/api/v1`
- URL: `https://statable.com/api/v1/sites`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/statable/apis/statable-stats-api/docs/sites/post.ts> (a function with typed inputs; its response is checked against the schema)

Creates a site and returns everything needed to install tracking, so provisioning is one round-trip. Requires an ALL-SITES key: a single-site key gets 403 `key_not_scoped`, since the new site would be outside its own scope. A duplicate url is 409 `site_exists` — unlike the dashboard, which permits it: for an agent a duplicate is nearly always a retry that lost its response. The first non-hobby site starts the trial subscription exactly as the dashboard does. Send an `Idempotency-Key` to make retries safe.

## Headers

- `Idempotency-Key` string

## Request body

- CreateSiteRequest
  - `url` string, required
  - `timezone` string: IANA zone. Omitted = the server default.
  - `hobby` boolean: Request a hobby site. The domain must match a configured hobby suffix.

## Response `200`

The created site, with its script URL and snippet.

- SiteDetail
  - `site_id` integer, required
  - `name` string, required: The site url as stored.
  - `hash` string, required: Same token as in SiteSummary.hash.
  - `timezone` string, required
  - `week_start` integer, required
  - `hobby` boolean, required
  - `script_url` string, required
  - `snippet` string, required: The ready-to-paste script tag.

## Other responses

- `400`: `invalid_request` — missing url, bad timezone, or a domain not eligible for hobby.
- `401`: Missing/malformed/invalid/revoked bearer token (`unauthorized`), or a key past its expiry (`key_expired`; the message names the expiry time).
- `403`: `insufficient_scope` or `key_not_scoped` (a single-site key).
- `404`: `write_disabled` — the write surface is off (`API_WRITE_ENABLED=false`). 404 rather than 403 so a disabled surface is not advertised.
- `409`: `site_exists` or `idempotency_conflict`.
- `429`: Hourly account (default 2000/h) or per-key (default 600/h) limit hit (`rate_limited`). Retry after the `Retry-After` seconds. The X-RateLimit-* headers report the window that tripped (account on an account limit).
- `500`: Unexpected server error (`internal`). Report it with the `request_id` from the body or the X-Request-ID header: it is what lets the exact log entry be found, and without it a 500 can only be matched by guessing at a time window.

---

[API](https://skmtc.dev/statable/apis/statable-stats-api.md) - [All operations](https://skmtc.dev/statable/apis/statable-stats-api/llms.txt) - [OpenAPI document](https://skmtc.dev/statable/apis/statable-stats-api/revisions/352b6d47c186?raw)
