Agency plan

The Husly API

Manage sites, pages, blog posts and media from your own script, from Zapier, or from your agency tooling. A versioned REST interface, authenticated with an API key.

Create an API key
What you need

The API is part of the Agency plan. The account owner creates a key under My account -> API keys. The full key is shown exactly ONCE, so store it right away.

Get started

curl
# Who am I? Returns the account id and the key's scopes.
curl https://api.husly.app/api/v1/me \
  -H "Authorization: Bearer hsly_live_ab12cd34ef56_..."

# List the sites on this account.
curl https://api.husly.app/api/v1/sites \
  -H "Authorization: Bearer hsly_live_ab12cd34ef56_..."

Authentication

Every request carries the API key. Both forms below work the same way - use whichever your tool supports. A key always starts with the prefix hsly_live_. A request with no key, or with a revoked one, gets a 401.

HTTP
Authorization: Bearer hsly_live_ab12cd34ef56_XXXXXXXXXXXXXXXXXXXXXXXX

# or

X-Api-Key: hsly_live_ab12cd34ef56_XXXXXXXXXXXXXXXXXXXXXXXX
The key is a secret

Never put a key in browser-side code or in a public repository - it reaches the whole account. Keep it in a server-side environment variable. A leaked key can be revoked immediately and replaced.

Scopes

A key carries exactly the scopes you ticked when you created it. A missing scope answers 403 insufficient_scope. Give each key only what it truly needs: a Zapier key that reads form replies has no business deleting sites.

Endpoints

Every path is relative to https://api.husly.app. :siteId is the site id from the GET /api/v1/sites response. The site must belong to your account - a stranger's site id answers 404, not 403, because a key must not learn that other accounts exist.

Account

Sites

Pages

Blog

Media

Forms

Redirects

Menus

Analytics

Webhooks

Example: publish a blog post

A post's body is a block tree - the same shape the builder saves. The simplest start is a single richtext block. Leave layoutJson out and you get an empty post you can finish in the builder.

curl
curl -X POST https://api.husly.app/api/v1/sites/SITE_ID/posts \
  -H "Authorization: Bearer hsly_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "kevadised-pakkumised",
    "title":   { "et": "Kevadised pakkumised", "en": "Spring offers" },
    "excerpt": { "et": "Lühike sissejuhatus.", "en": "A short intro." },
    "status": "published",
    "authorName": "Mari Maasikas",
    "layoutJson": {
      "contentWidth": "narrow",
      "blocks": [
        { "id": "b1", "type": "richtext", "props": {
            "content": { "et": "<p>Tere tulemast!</p>", "en": "<p>Welcome!</p>" } } }
      ]
    }
  }'

Errors

An error is always JSON shaped { "error": "code" }, sometimes with extra fields (required, limit and so on). The code is machine-readable and stable - branch on the code, never on the message.

Limits

Requests are capped per key per minute. Going over answers 429 with a Retry-After header, and the body's `limit` field says which cap applied. Plan quotas - number of sites, number of pages, storage - apply through the API as well: a script cannot walk around them.

Versioning

The current version is v1. We will not reshape a v1 response in a way that breaks your integration - new fields may appear, existing ones will not vanish. A breaking change would mean a new path (/api/v2/).

Frequently asked questions

Ready to start?

The API is part of the Agency plan. If you already have an account, a key is a few clicks away.

Create an API key