HTTP API
The StaticOwl HTTP API lets you manage sites, content, content types, templates, and builds from your own code. It's the same surface the dashboard uses, so anything you do by hand you can automate: model a content type, load records, render templates, and publish to your CDN.
- Base URL:
https://app.staticowl.com - Format: JSON request and response bodies (
Content-Type: application/json). - Auth: Send a StaticOwl API key as a bearer token on every request (except the public health check).
Create a key in your account settings and pass it on the Authorization header:
curl https://app.staticowl.com/api/sites \
-H "Authorization: Bearer $STATICOWL_API_KEY"
Keys can be scoped to specific sites, so a key used by a deploy script or an agent only reaches the sites you grant it. Store the key as a secret and rotate it from account settings if it leaks.
Conventions
A few rules apply across the whole API.
| Convention | Detail |
|---|---|
| Authentication | Authorization: Bearer <key> on every call. A missing or invalid key returns 401. |
| Selecting a site | Most endpoints act on one site. Send its id on the X-Site-Id header. Get the id from GET /api/sites. |
| Request bodies | JSON. Set Content-Type: application/json on POST, PUT, and PATCH. |
| Partial updates | PATCH and PUT accept only the fields you want to change. Omitted fields are left as they are. |
| Errors | Non-2xx responses return a JSON body with a code and a human-readable message. |
Common status codes: 200/201 on success, 204 on delete, 400 for a bad
request body, 401 for auth, 404 for a missing resource, and 413 when an
import payload is too large (the response includes a suggested batch size).
Sites
A site is one published property — its content, types, templates, and build settings.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/sites |
List the sites your key can reach. |
| POST | /api/sites |
Create a site. |
| GET | /api/sites/{id} |
Get one site's settings. |
| PUT | /api/sites/{id} |
Update site settings (partial). |
POST /api/sites takes name (required) and optional slug, publicUrl,
description, mantra, a theme object (primary, accent), and a
starterKit (designer, developer, author, or none) that seeds a site
with a sensible set of types and templates.
curl -X POST https://app.staticowl.com/api/sites \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Marketing Site",
"slug": "marketing",
"theme": { "primary": "#1a1a2e", "accent": "#e94560" },
"starterKit": "author"
}'
Content types
A content type defines a kind of record — its fields, its URL pattern, and whether it renders its own pages. See Content modeling for the full field-type reference.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/types |
List content types for the site. |
| POST | /api/types |
Create a content type. |
| GET | /api/types/{name} |
Get one content type. |
| PATCH | /api/types/{name} |
Update a content type (partial). |
All four require the X-Site-Id header. A type carries fields, a
routePattern (e.g. /blog/:slug), and emitPages (whether items of this type
get their own rendered page).
curl -X POST https://app.staticowl.com/api/types \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "article",
"routePattern": "/blog/:slug",
"emitPages": true,
"fields": [
{ "name": "title", "type": "text", "required": true },
{ "name": "slug", "type": "text", "required": true },
{ "name": "body", "type": "richtext" }
]
}'
Partial updates replace the whole field they touch. A
PATCHthat sendsfieldsreplaces the entire field list. To change one field, send the full list with your edit applied.
Content
Content items are the records of a given type.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/content/{type} |
List items of a type. |
| POST | /api/content/{type} |
Create an item. |
| GET | /api/content/{type}/{id} |
Get one item. |
| PATCH | /api/content/{type}/{id} |
Update an item (partial). |
| DELETE | /api/content/{type}/{id} |
Delete an item. |
All require X-Site-Id. List supports limit, offset, and withMeta=1 (adds
record metadata such as timestamps alongside each item). The create and update
bodies are a plain object whose keys match the type's fields.
# Create
curl -X POST https://app.staticowl.com/api/content/article \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{ "title": "Hello world", "slug": "hello-world", "body": "<p>First post.</p>" }'
# List, newest page first, with metadata
curl "https://app.staticowl.com/api/content/article?withMeta=1&limit=20&offset=0" \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID"
Writing content stages it. It goes live when you build and deploy (below). For a production build, content must be published first — do that from the dashboard or through an AI agent (see Connect your AI).
Bulk import
For loading many records or relationships at once.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/import/json |
Load many records of one type. |
| POST | /api/import/edges |
Create relationships between existing records. |
import/json takes typeName, a records array, and options batchSize
(1–500, default 100), skipExisting, and dryRun. If a payload is too large the
API returns 413 with a suggested batch size and call count — split the records
and retry.
curl -X POST https://app.staticowl.com/api/import/json \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{
"typeName": "article",
"batchSize": 100,
"skipExisting": true,
"records": [
{ "title": "One", "slug": "one" },
{ "title": "Two", "slug": "two" }
]
}'
import/edges links records that already exist. Each edge has a from, an edge
label (uppercase), a to, and optional props; batchSize defaults to 500.
curl -X POST https://app.staticowl.com/api/import/edges \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{ "edges": [ { "from": "article/hello-world", "edge": "WRITTEN_BY", "to": "author/jane" } ] }'
Templates
Templates render content into HTML. They use LiquidJS syntax and are versioned — each update creates a new version at head.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/templates |
List templates for the site. |
| POST | /api/templates |
Create a template. |
| GET | /api/templates/{name} |
Get a template at its current version. |
| PUT | /api/templates/{name} |
Update a template (creates a new version). |
Send name and the template body as content (html is accepted as an alias).
Set boundType to bind the template to a content type so its pages render with
it.
curl -X POST https://app.staticowl.com/api/templates \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "article",
"boundType": "article",
"content": "<article><h1>{{ title }}</h1>{{ body }}</article>"
}'
Builds
A build renders the site and deploys it to the CDN.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/build |
Build and deploy a site. |
| GET | /api/build/status/{buildId} |
Poll an async build. |
Both require X-Site-Id. Pick an environment with env (e.g. prod) and a
mode: full, incremental, or validate. With no mode, the API runs a
full build; pass incremental to rebuild only what changed (much faster on a
large site). validate is a dry run that renders and reports without deploying.
Pass async=1 to return immediately with a buildId you can poll.
# Synchronous production build
curl -X POST "https://app.staticowl.com/api/build?env=prod" \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID" \
-H "Content-Type: application/json" \
-d '{ "env": "prod", "mode": "full" }'
# Async build, then poll
curl -X POST "https://app.staticowl.com/api/build?env=prod&async=1" \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID"
# -> 202 { "buildId": "b_123", "statusUrl": "/api/build/status/b_123" }
curl https://app.staticowl.com/api/build/status/b_123 \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID"
A synchronous build returns a build result describing what rendered and
deployed. An async build returns 202 with a buildId; poll the status URL
until it finishes.
Audit
If your site uses the compliance features, you can verify that its content trail is intact.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/audit/verify |
Check that a site's audit trail is intact. |
| POST | /api/audit/checklist |
Run the compliance checklist and return its rows. |
Both take a required graph query parameter — the site's content-store name,
shown in your site settings. verify returns a status of ok, broken, or
empty.
curl "https://app.staticowl.com/api/audit/verify?graph=$SITE_GRAPH" \
-H "Authorization: Bearer $STATICOWL_API_KEY"
Search index maintenance
Related-content and similarity features read a per-type index. After a large import you can rebuild it for one type. The call is idempotent.
curl -X POST "https://app.staticowl.com/api/maintenance/embeddings/materialize?type=article" \
-H "Authorization: Bearer $STATICOWL_API_KEY" \
-H "X-Site-Id: $SITE_ID"
Content label integrity
Every content item carries a shared label so site-wide features — the sitemap and feeds, search, the compliance scan, and content counts — can find it. A drift check reports how many items are missing it; the repair stamps only the ones that need it. Both are read- and write-safe to re-run (idempotent).
# How many items are missing the shared label?
curl "https://app.staticowl.com/api/maintenance/content-labels/drift" \
-H "Authorization: Bearer $STATICOWL_API_KEY" -H "X-Site-Id: $SITE_ID"
# -> { "total": 186, "labeled": 180, "missing": 6, "byType": [ ... ] }
# Repair (stamps only the missing items, in safe batches).
curl -X POST "https://app.staticowl.com/api/maintenance/content-labels/repair" \
-H "Authorization: Bearer $STATICOWL_API_KEY" -H "X-Site-Id: $SITE_ID"
# -> { "stampedNow": 6, "perType": [ ... ], "errors": [] }
Health
A public liveness check, no auth required. Useful for uptime monitoring.
curl https://app.staticowl.com/api/health
# -> { "status": "ok", ... }
Working beyond the REST API
This page covers the core content lifecycle. Media, forms, redirects, custom domains, webhooks, SEO settings, and releases have their own REST endpoints too — each is documented in its own guide (Media, Forms, Redirects, Distribution) — and all of them are available from the dashboard and through the MCP server as well.
Everything above — and those additional resources — is also available to AI agents through the StaticOwl MCP server:
- Endpoint:
https://app.staticowl.com/api/mcp - Tools: 352 tools across 13 toolsets (content, media, builds, SEO, forms, domains, regional markets and translations, and more).
- Auth: the same StaticOwl API key you use here.
Point a compatible agent at the endpoint and it can model types, write and publish content, and run builds on your behalf. See Connect your AI for setup.
See also
- Zero to deployed — the end-to-end walk-through, from first site to live pages.
- Content modeling — field types, routes, and relationships in depth.
- Connect your AI — using the MCP server with an AI agent.
- Docs home — the full documentation set.
- Distribution — webhooks and scheduled social posts.