Back

A simple Typecho API plugin

| Updated at |

TypechoApi — REST API Documentation

Base URL: https://your-site.com/typecho-api/v1
Content-Type: application/json (except uploads: multipart/form-data)
Response Header: X-Typecho-Api-Version: 1.0.0


Authentication

All endpoints require authentication. Two methods are supported, controlled by the authMode plugin setting.

Basic Auth

Authorization: Basic <base64(username:password)>

Uses Typecho's user database. Only users with roles listed in allowedRoles (default: administrator,editor) are permitted.

Bearer Token

X-Typecho-Token: <token-secret>

Tokens are configured in plugin settings as label:secret pairs (one per line).

IP Allow-List (Optional)

If configured, only listed IPv4/IPv6 addresses can access any endpoint. All others receive 403.


Endpoints

1. List Posts

GET /typecho-api/v1/posts

Query Parameters:

Parameter Type Default Description
page int 1 Page number
pageSize int 20 Items per page (max 100)
status string — Filter: publish, draft, private, waiting, hidden
authorId int — Filter by author ID
search string — Full-text search in title and content

Response 200:

{
  "data": [
    {
      "id": 12,
      "title": "Hello World",
      "slug": "hello-world",
      "status": "publish",
      "text": "<!--markdown-->\n\n# Hello",
      "markdown": "# Hello",
      "contentFormat": "markdown",
      "authorId": 1,
      "authorName": "Admin",
      "allowComment": true,
      "allowPing": true,
      "allowFeed": true,
      "created": 1773102541,
      "createdAt": "2026-03-10T00:29:01+00:00",
      "modified": 1773102541,
      "modifiedAt": "2026-03-10T00:29:01+00:00",
      "categories": [
        { "id": 2, "name": "News", "slug": "news", "type": "category" }
      ],
      "tags": [
        { "id": 5, "name": "api", "slug": "api", "type": "tag" }
      ],
      "fields": {},
      "excerpt": "Hello content excerpt..."
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 20,
    "total": 42
  }
}

2. Get Single Post

GET /typecho-api/v1/post?id={id}

Response 200: Single post object wrapped in { "data": { ... } } (same shape as list items, without excerpt).

Response 404: { "error": { "code": 404, "message": "Post not found." } }


3. Create Post

POST /typecho-api/v1/posts

Request Body:

Field Type Required Default Description
title string yes — Post title
markdown string yes* — Markdown content (highest priority)
text string yes* — Raw text content (second priority)
content string yes* — Content fallback (lowest priority)
status string no "draft" publish / draft / private / waiting / hidden
authorId int no config default Author user ID
created int no now Unix timestamp
slug string no auto-generated URL slug (uniquified automatically)
password string no — Password-protect the post
allowComment bool no true Allow comments
allowPing bool no true Allow pingbacks
allowFeed bool no true Include in RSS feed
categories array or string no — Category names, slugs, IDs, or objects
tags array or string no — Tag names, slugs, IDs, or objects
fields object no — Custom fields { "key": "value" }

*One of markdown, text, or content is required.

Example:

curl -X POST https://your-site.com/typecho-api/v1/posts \
  -H "X-Typecho-Token: my-secret-token" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Hello from API",
    "markdown": "# Heading\n\n**Bold** content here.",
    "status": "publish",
    "categories": ["News", "Announcements"],
    "tags": ["api", "typecho"],
    "fields": { "priority": 5 }
  }'

Response 201:

{
  "message": "Post created.",
  "data": { /* full post object */ }
}

Taxonomy Input Formats

Categories and tags accept flexible input:

// Comma-separated string
"categories": "News, Announcements"

// Array of names
"categories": ["News", "Announcements"]

// Array of objects
"categories": [{ "name": "News", "slug": "news" }, { "id": 2 }]

// Mixed
"categories": ["News", { "name": "Announcements", "slug": "announcements" }]

Non-existent categories/tags are auto-created.


4. Update Post (Full Replace)

PUT /typecho-api/v1/post?id={id}

Replaces all fields. Same body format as Create.

Response 200: { "message": "Post updated.", "data": { ... } }


5. Update Post (Partial)

PATCH /typecho-api/v1/post?id={id}

Updates only the provided fields; all others are preserved.

Example — change only status:

curl -X PATCH https://your-site.com/typecho-api/v1/post?id=12 \
  -H "X-Typecho-Token: my-secret-token" \
  -H "Content-Type: application/json" \
  -d '{ "status": "publish" }'

Response 200: { "message": "Post updated.", "data": { ... } }


6. Delete Post

DELETE /typecho-api/v1/post?id={id}

Hard-deletes the post and cascades to relationships, comments, and custom fields.

Response 200:

{
  "message": "Post deleted.",
  "data": { "id": 12 }
}

7. Upload Image

POST /typecho-api/v1/upload
Content-Type: multipart/form-data

Form Fields:

Field Type Required Description
image file yes Image file
destination string no local (default), bunny, r2, or both

Allowed types: JPEG, PNG, GIF, WebP, SVG, BMP, AVIF
Max size: 10 MB

Example:

curl -X POST https://your-site.com/typecho-api/v1/upload \
  -H "X-Typecho-Token: my-secret-token" \
  -F "[email protected]" \
  -F "destination=r2"

Response 200:

{
  "message": "Image uploaded.",
  "data": {
    "url": "https://cdn.example.com/2026/03/photo-a1b2c3d4.jpg",
    "path": "/usr/uploads/2026/03/photo-a1b2c3d4.jpg",
    "filename": "photo-a1b2c3d4.jpg",
    "size": 45678,
    "mimeType": "image/jpeg",
    "destination": "r2",
    "embed": {
      "markdown": "![](https://cdn.example.com/2026/03/photo-a1b2c3d4.jpg)",
      "html": "<img src=\"https://cdn.example.com/2026/03/photo-a1b2c3d4.jpg\" alt=\"\" />"
    },
    "urls": {
      "r2": "https://r2cdn.example.com/2026/03/14/photo.jpg",
      "bunny": null
    }
  }
}

Storage destinations:

Value Behavior
local Saves to Typecho's local /usr/uploads/ directory
bunny Uploads to BunnyCDN storage zone
r2 Uploads to Cloudflare R2 bucket
both Uploads to R2 and BunnyCDN concurrently; returns BunnyCDN URL as primary

8. Increment Agree

POST /typecho-api/v1/agree

Increments the agree counter on a random published post. Has a 10% chance of skipping (no-op).

Response 200:

{
  "data": {
    "updated": true,
    "postId": 42,
    "agree": 7,
    "skipped": false
  }
}

Error Responses

All errors follow a consistent format:

{
  "error": {
    "code": 401,
    "message": "Authentication failed."
  }
}

Error Code Reference

Code Message When
400 Query parameter 'id' is required. Missing ?id= on single-post endpoints
400 Request body must be valid JSON. Malformed JSON body
400 No image uploaded or upload error Missing file in upload request
401 Authentication failed. Invalid credentials
403 The authenticated user role is not allowed for API access. User role not in allowedRoles
403 This IP address is not allowed. IP not in allow-list
404 Post not found. Post ID doesn't exist
405 Method not allowed. Wrong HTTP method for endpoint
422 Field 'title' is required. Missing required field
422 Unsupported image type: {type} File MIME not in allowed list
422 Image exceeds maximum size of 10 MB. Upload too large
422 Invalid destination. Unknown storage destination
422 Invalid post status. Status not in allowed list
500 Post was created but could not be reloaded. DB write succeeded, read failed
500 Failed to create upload directory. Server filesystem error
500 Failed to save uploaded file. move_uploaded_file() failed
502 Cloud upload failed: {reason} R2/BunnyCDN upload error

Plugin Configuration

Configure in Typecho admin panel under Plugins → TypechoApi → Settings.

Authentication

Setting Type Default Description
authMode radio both basic, bearer, or both
allowedRoles text administrator,editor Comma-separated allowed user roles
bearerTokens textarea — label:secret pairs, one per line
allowedIps textarea — IPv4/IPv6 addresses, one per line
defaultAuthorId text 1 Default author for bearer token requests

Cloud Storage

Setting Type Default Description
storageDestination radio local Default upload destination
bunnyAccessKey text — BunnyCDN API password
bunnyStorageZone text — BunnyCDN storage zone name
bunnyStorageHost text https://syd.storage.bunnycdn.com BunnyCDN region endpoint
bunnyPublicHost text — BunnyCDN public URL prefix
r2AccessKeyId text — Cloudflare R2 access key ID
r2AccessKeySecret text — Cloudflare R2 secret key
r2Bucket text — R2 bucket name
r2Endpoint text — R2 S3-compatible endpoint
r2PublicUrl text — R2 public URL prefix
r2RootFolder text — Optional path prefix inside bucket

Quick Reference

Action Method Endpoint Auth Required
List posts GET /typecho-api/v1/posts Yes
Get post GET /typecho-api/v1/post?id=N Yes
Create post POST /typecho-api/v1/posts Yes
Replace post PUT /typecho-api/v1/post?id=N Yes
Partial update PATCH /typecho-api/v1/post?id=N Yes
Delete post DELETE /typecho-api/v1/post?id=N Yes
Upload image POST /typecho-api/v1/upload Yes
Increment agree POST /typecho-api/v1/agree Yes

No comments yet

Leave a comment

About me

I live in Tasmania, an island a little far from the rest of the world and very close to the sea.
Father of two little girls, have two puppies.

Work

Work in IT industry, AI is my working partner.