API Documentation

JSON API (v1)

Recommended for apps and scripts: predictable JSON, rich filters, and explicit blog selection via blog_id on the query string when you are not using the selected blog.

BASE URL https://scribbles.page/api/v1
Note: Replace YOUR_API_KEY in examples with a key from API settings. Paths below are shown from the base URL.

Discover blogs and metadata

GET https://scribbles.page/api/v1/info

Returns API metadata and the blogs available to your account.

Example Request

curl "https://scribbles.page/api/v1/info" \
  -H "Authorization: Bearer YOUR_API_KEY"

List posts

GET https://scribbles.page/api/v1/posts

List, filter, and paginate posts. List responses omit heavy body fields unless you opt in with include_content.

Example Request

curl "https://scribbles.page/api/v1/posts?limit=10&sort=-published_at" \
  -H "Authorization: Bearer YOUR_API_KEY"

curl "https://scribbles.page/api/v1/posts?include_content=true&limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"

Query parameters

Name Type Required Description
blog_id integer No Target a specific blog by id.
status string No published (default), draft, archived, or all.
type string No posts or pages.
category string No Filter to one category (name).
category_id integer No Filter to one category (id).
exclude_category string No Exclude one category (name).
exclude_category_id integer No Exclude one category (id).
uncategorized boolean No true for posts with no category.
search string No Search title and content.
from_date date No Start of date range filter.
to_date date No End of date range filter.
page integer No Page number for pagination.
limit integer No Page size (max 100).
sort string No Sort field; prefix with - for descending (title, created_at, updated_at, published_at).
include_content boolean No When true, list items include HTML/text bodies.
Note: Do not combine category filters with exclude_category* or uncategorized=true; those combinations return 400 Bad Request.

Create and update posts

POST https://scribbles.page/api/v1/posts

Create a post with a JSON body wrapped under post. Use PUT or PATCH on /posts/:id to update.

Example Request

curl -X POST "https://scribbles.page/api/v1/posts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"post":{"title":"Hello","content":"This is **markdown**.","content_type":"markdown","status":"published"}}'
PATCH https://scribbles.page/api/v1/posts/:id

Example (update)

curl -X PATCH "https://scribbles.page/api/v1/posts/123" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"post":{"title":"Updated title"}}'
DELETE https://scribbles.page/api/v1/posts/:id

Example (delete)

curl -X DELETE "https://scribbles.page/api/v1/posts/123" \
  -H "Authorization: Bearer YOUR_API_KEY"

Publish and unpublish

PATCH https://scribbles.page/api/v1/posts/:id/publish
PATCH https://scribbles.page/api/v1/posts/:id/unpublish

Example Request

curl -X PATCH "https://scribbles.page/api/v1/posts/123/publish" \
  -H "Authorization: Bearer YOUR_API_KEY"

curl -X PATCH "https://scribbles.page/api/v1/posts/123/unpublish" \
  -H "Authorization: Bearer YOUR_API_KEY"

Attachments and rich HTML

POST https://scribbles.page/api/v1/attachments

The usual flow is two requests: upload the image, then create (or update) a post that embeds the returned snippet and sets alt text. Upload with multipart form data.

1. Upload the image

curl -X POST "https://scribbles.page/api/v1/attachments" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "blog_id=YOUR_BLOG_ID" \
  -F "attachment[file][email protected]" \
  -F "attachment[caption]=Optional caption"

Copy html (the ActionText snippet) and filename from the response:

Upload response

{
  "attachment": {
    "html": "<action-text-attachment sgid=\"SGID_FROM_UPLOAD\" content-type=\"image/jpeg\" filename=\"photo.jpg\" caption=\"Optional caption\"></action-text-attachment>",
    "filename": "photo.jpg",
    "media_type": "image"
  }
}

Embed the html value in content and key alt_text by the filename. Send the body as HTML — do not use content_type=markdown for attachment HTML.

2. Create the post with alt text

curl -X POST "https://scribbles.page/api/v1/posts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "blog_id": "YOUR_BLOG_ID",
    "post": {
      "title": "My post with an image",
      "content": "<p>Intro.</p>ACTION_TEXT_ATTACHMENT_HTML<p>Outro.</p>",
      "status": "published",
      "alt_text": { "photo.jpg": "A sunlit desk with a notebook" }
    }
  }'
Note: Replace ACTION_TEXT_ATTACHMENT_HTML with the html value from the upload response. Embedding it makes the image a real attachment, so the post card/preview image and caption work exactly like the editor.
Note: Send attachment HTML as content, not content_type=markdown, which can strip action-text-attachment blobs during conversion.
Note: Images: JPEG, PNG, GIF, WebP, AVIF up to 20 MB. Video: MP4, MOV, WebM up to 250 MB. SVG is not accepted.

See Alt text for describing images.

Alt text

Set per-image alt text the same way the editor does by sending post[alt_text] when you create or update a post, keyed by the attachment filename returned by the upload endpoint. It is stored on the post and comes back as alt on each attachment in post responses.

Object form (filename → alt)

{"post":{"alt_text":{"photo.jpg":"A sunlit desk with a notebook","diagram.png":"Architecture overview"}}}

Array form

{"post":{"alt_text":[{"filename":"photo.jpg","alt":"A sunlit desk with a notebook"}]}}
Note: Key by the exact filename returned from the upload (for example photo.jpg).
Note: Sending alt_text replaces the post's existing alt text, so include every image you want described. Omit the field on update to leave existing alt text unchanged.
Note: If an image has no alt_text entry, its upload caption is used as the rendered alt; with neither, the image has no alt text.

Response shape (sketch)

Single post payloads wrap attributes under post, including nested content (html, text), blog, category, meta, and optional attachments. Lists return posts plus pagination (page, limit, total, pages).

Single post response

{
  "post": {
    "id": 123,
    "title": "My post with an image",
    "slug": "my-post-with-an-image",
    "status": "published",
    "type": "post",
    "published_at": "2026-06-18T08:30:00Z",
    "created_at": "2026-06-18T08:30:00Z",
    "updated_at": "2026-06-18T08:30:00Z",
    "url": "https://your-blog.example/post/my-post-with-an-image",
    "blog": { "id": 9, "title": "My blog", "slug": "my-blog" },
    "category": { "id": 4, "name": "Notes", "slug": "notes" },
    "meta": {
      "hidden": false,
      "hide_in_rss": false,
      "no_index_no_follow": false,
      "show_contact_form": false,
      "komments_disabled": false,
      "archived": false,
      "skip_archive": false,
      "is_shared": false,
      "share_id": null
    },
    "content": { "html": "<p>Intro.</p>…", "text": "Intro. …" },
    "attachments": [
      {
        "id": 55,
        "blob_id": 88,
        "filename": "photo.jpg",
        "content_type": "image/jpeg",
        "byte_size": 46956,
        "media_type": "image",
        "url": "https://…/photo.jpg",
        "thumbnail": "https://…/photo.jpg",
        "alt": "A sunlit desk with a notebook"
      }
    ]
  }
}

Errors

  • 401 — Missing or invalid API key.
  • 403 — Authenticated but not allowed for the resource or action.
  • 404 — Unknown blog or post.
  • 400 — Bad query combination (for example category filters that exclude each other) or missing upload fields.
  • 422 — Validation errors; JSON body often includes errors / message.

← API overview · Micropub guide →