JotBirdJotBird
Open app
API Reference

JotBird API

Turn Markdown into a hosted web page with a single API call.

Overview

Introduction

The JotBird API lets you turn Markdown into shareable web pages using scripts, CI pipelines, or any HTTP client. Send Markdown, get back a public URL. All you need is a free JotBird account and an API key.

All endpoints accept and return JSON. The base URL for all requests is:

https://www.jotbird.com

If you prefer a CLI, the jotbird command wraps these endpoints with file tracking and slug mapping. Install it from npm:

npm install -g jotbird

An OpenAPI spec is also available for code generation and tooling.

Security

Authentication

All API requests require a personal API key, passed as a Bearer token in the Authorization header:

Authorization: Bearer jb_your_api_key_here

Generating a key

Sign up for a free account at jotbird.com, then generate an API key in two ways:

  • CLI: Run jotbird login to open the browser and generate a key automatically.
  • Browser: Open Account Settings from the account menu in the web app and generate a key from the API Keys section.

API keys start with jb_ and are shown only once at creation. Store your key securely — if lost, generate a new one.

Key limits

Each account can have up to 10 API keys. If you reach the limit, revoke an existing key before generating a new one.

Revoking a key

To revoke an API key, open Account Settings from your account menu in the web app. Each key shows its prefix, creation date, and last usage. Click Revoke to permanently delete a key. Revoked keys stop working immediately.

Reference

Rate Limits

Publish requests are rate-limited per account on a rolling hourly window. Free accounts also have a cap on the number of active documents published via API. Active documents are documents with a live public URL that have not expired. Expired documents do not count toward the limit.

LimitFree AccountPro Account
Publishes per hour10100
Active documents10Unlimited

Response headers

Every publish response includes rate limit information:

HeaderDescription
X-RateLimit-LimitMaximum requests per hour for your account
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets
Retry-AfterSeconds until you can retry (only on 429 responses)

Pro accounts are for individual use only, not enterprise use. If you exceed the rate limit, the API returns a 429 status with a Retry-After header indicating when to retry.

Endpoint

Publish a document

POST/api/v1/publish

Publish a new Markdown document or update an existing one. The server renders your Markdown to HTML and hosts it at a shareable URL. Free accounts get 90-day links. Pro makes links permanent.

Request body

ParameterTypeRequiredDescription
markdownstringYesThe Markdown content to publish.
titlestringNoDocument title. If omitted, extracted from the first H1 in the Markdown.
slugstringNoSlug of an existing document to update. Required when namespaced: true.
namespacedbooleanNoWhen true, publish under your username at share.jotbird.com/@username/slug. Requires Pro and a username set in Account Settings. See Namespaced URLs.
curl -X POST https://www.jotbird.com/api/v1/publish \
  -H "Authorization: Bearer jb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Hello World\n\nThis is my document."
  }'

Updating an existing document

To update a document you've already published, include its slug in the request body. The content at that URL is replaced with the new Markdown.

curl -X POST https://www.jotbird.com/api/v1/publish \
  -H "Authorization: Bearer jb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Hello World\n\nUpdated content goes here.",
    "slug": "bright-calm-meadow"
  }'

The response returns 200 instead of 201, and "created": false.

Response

Returns 201 for new documents, 200 for updates.

{
  "slug": "bright-calm-meadow",
  "username": null,
  "url": "https://share.jotbird.com/bright-calm-meadow",
  "title": "Hello World",
  "expiresAt": "2026-05-10T12:00:00.000Z",
  "ttlDays": 90,
  "created": true
}

A URL slug is generated automatically for new documents. Re-publishing with the same slug updates the document in place rather than creating a new URL. Pro accounts get permanent documents — expiresAt will be null.

slug can't name a new flat document. It identifies a document you already own. If you pass a slug that matches no document on your account, it is ignored and the document is published at an auto-generated slug — read the slug in the response rather than assuming you got the one you sent. To choose a URL, publish a namespaced document (Pro).

Image uploads are not supported through the API. Markdown image references (e.g. ![alt](url)) will render only if they point to externally-hosted images.

Free accounts are limited to 10 active documents. Updating an existing document does not count toward this limit. See Rate Limits for details.

Pro feature

Namespaced URLs

Pro users who have set a username can publish documents at permanent, human-readable URLs like share.jotbird.com/@username/my-page. Set your username in Account Settings.

Pass "namespaced": true in the publish request body to publish at your username. A slug is required — the API will not auto-generate one for namespaced documents. If a document with that slug already exists in your namespace, it is updated in place.

curl -X POST https://www.jotbird.com/api/v1/publish \
  -H "Authorization: Bearer jb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# My Page\n\nPublished at a custom URL.",
    "slug": "my-page",
    "namespaced": true
  }'

The response includes a username field and a namespaced URL:

{
  "slug": "my-page",
  "username": "clayton-myers",
  "url": "https://share.jotbird.com/@clayton-myers/my-page",
  "title": "My Page",
  "expiresAt": null,
  "ttlDays": null,
  "created": true
}

The list endpoint always returns the correct URL for each document and includes a username field ( null for documents without a username).

Namespaced publishing requires a Pro subscription and a username set in Account Settings. Transitioning an existing document to a namespaced URL is a web-only flow — use Page Settings in the editor.

Endpoint

List documents

GET/api/v1/documents

Retrieve all published documents for your account. Only documents with an active public URL are returned.

Request

No request body is required.

curl https://www.jotbird.com/api/v1/documents \
  -H "Authorization: Bearer jb_your_api_key_here"

Response

{
  "documents": [
    {
      "slug": "my-page",
      "username": "clayton-myers",
      "title": "My Page",
      "url": "https://share.jotbird.com/@clayton-myers/my-page",
      "source": "api",
      "updatedAt": "2026-04-20T10:00:00.000Z",
      "expiresAt": null
    },
    {
      "slug": "bright-calm-meadow",
      "username": null,
      "title": "Hello World",
      "url": "https://share.jotbird.com/bright-calm-meadow",
      "source": "cli",
      "updatedAt": "2026-02-09T14:30:00.000Z",
      "expiresAt": "2026-05-10T14:30:00.000Z",
      "theme": "default",
      "hideBranding": false,
      "visibility": "unlisted",
      "tags": []
    }
  ]
}

Each document also includes its current page settings — theme, hideBranding, visibility, and tags — see Page settings.

Results are ordered by most recently updated. The username field is null for documents without a username. The url field always reflects the correct public URL. The source field indicates how the document was created: "cli", "web", or "api".

Endpoint

Page settings

GET/api/v1/documents/:slug/settings
PATCH/api/v1/documents/:slug/settings

Read and change a published document's page settings — theme, branding, and visibility — without republishing. For documents at @username/slug URLs, add ?namespaced=true. You can also address a document by its stable id with ?documentId=<uuid> (returned by the publish endpoint) — it takes precedence over the path slug and survives slug changes.

Read settings

curl https://www.jotbird.com/api/v1/documents/bright-calm-meadow/settings \
  -H "Authorization: Bearer jb_your_api_key_here"
{
  "slug": "bright-calm-meadow",
  "username": null,
  "url": "https://share.jotbird.com/bright-calm-meadow",
  "title": "Hello World",
  "theme": "default",
  "hideBranding": false,
  "visibility": "unlisted",
  "tags": [],
  "expiresAt": "2026-05-10T14:30:00.000Z"
}

Update settings

Send only the settings you want to change — everything else is left as-is. The response is the full updated settings state.

ParameterTypeRequiredDescription
themestringNoOne of "default", "minimal", "essay", "terminal". Non-default themes require Pro.
hideBrandingbooleanNoRemove the JotBird watermark from the published page. Requires Pro to enable; anyone can disable.
visibilitystringNoOne of "unlisted", "public", "password". Public allows search-engine indexing and lists the page in the sitemap. Password requires Pro.
passwordstringNoPro only. Required with (and only valid with) visibility "password". Write-only — never returned by the API.
curl -X PATCH https://www.jotbird.com/api/v1/documents/bright-calm-meadow/settings \
  -H "Authorization: Bearer jb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"theme": "essay", "visibility": "public"}'

To password-protect a page:

curl -X PATCH https://www.jotbird.com/api/v1/documents/bright-calm-meadow/settings \
  -H "Authorization: Bearer jb_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"visibility": "password", "password": "correct-horse-battery"}'

The whole patch is validated before anything is written — an invalid or Pro-gated field rejects the entire request (the error names the offending setting). If a write fails partway through applying a multi-field patch (rare), the response reports the error and earlier fields may already be applied — retrying the same PATCH is safe and completes the update. Free accounts can always switch between unlisted and public and clear Pro settings.

Settings updates share the hourly publish rate-limit tiers (free: 10/hour, Pro: 100/hour) in a separate bucket, so they don't consume your publish quota. Every PATCH is charged, including rejected ones, and responses carry the same X-RateLimit-* headers as publish. Reading settings is not rate-limited.

Visibility changes can take up to about a minute to reach the published page. The API response and a subsequent GET reflect the change immediately (the API is the source of truth), but the live page at share.jotbird.com — its search-indexing state and password gate — updates as the edge cache refreshes. If you verify by fetching the page, poll for up to a minute rather than treating a stale response as a failed request. Theme and branding changes apply right away.

One exception: turning password protection on takes effect immediately. The gate fails closed, so a page is never left unprotected while the change propagates. It's removing or relaxing protection — switching to unlisted or public — that waits on the cache, so a page can briefly keep asking for a password after you remove it.

Endpoint

Remove a document

DELETE/api/v1/documents?slug=:slug

Permanently delete a document. This removes the public URL, the stored content, and all associated image references. This action cannot be undone.

Query parameters

ParameterTypeRequiredDescription
slugstringYesThe slug of the document to remove.
namespacedbooleanNoWhen true, remove the document at @username/slug. Omit for documents without a username. Requires Pro and a username.
curl -X DELETE "https://www.jotbird.com/api/v1/documents?slug=bright-calm-meadow" \
  -H "Authorization: Bearer jb_your_api_key_here"

To remove a namespaced document:

curl -X DELETE "https://www.jotbird.com/api/v1/documents?slug=my-page&namespaced=true" \
  -H "Authorization: Bearer jb_your_api_key_here"

Response

{
  "ok": true
}

Permanent action

This permanently deletes the document from your account. There is no way to recover a removed document.

Reference

Errors

All error responses return a JSON object with an error field containing a human-readable message:

{
  "error": "Missing markdown field"
}

Status codes

CodeMeaningExampleCommon causes
400Bad Request"Missing markdown field"Invalid JSON, missing required fields, invalid slug format
401Unauthorized"Invalid API key"Missing or expired API key, malformed Bearer token
403Forbidden"Document not owned by user"
"Document limit reached (10)..."
Document owned by another account, or free-tier document cap reached
429Too Many Requests"Rate limit exceeded. Try again in 1800 seconds."More than 10 (free) or 100 (pro) publishes per hour
413Payload Too Large"Document too large to publish — the formatted page exceeds the 512 KB limit. …"Rendered HTML exceeds 512 KB
503Service Unavailable"Failed to allocate document slug"Database temporarily unavailable, slug allocation failed

Also available as an agent skill: npx skills add jotbirdhq/skill