A simple Typecho API plugin

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:

ParameterTypeDefaultDescription
pageint1Page number
pageSizeint20Items per page (max 100)
statusstringFilter: publish, draft, private, waiting, hidden
authorIdintFilter by author ID
searchstringFull-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:

FieldTypeRequiredDefaultDescription
titlestringyesPost title
markdownstringyes*Markdown content (highest priority)
textstringyes*Raw text content (second priority)
contentstringyes*Content fallback (lowest priority)
statusstringno"draft"publish / draft / private / waiting / hidden
authorIdintnoconfig defaultAuthor user ID
createdintnonowUnix timestamp
slugstringnoauto-generatedURL slug (uniquified automatically)
passwordstringnoPassword-protect the post
allowCommentboolnotrueAllow comments
allowPingboolnotrueAllow pingbacks
allowFeedboolnotrueInclude in RSS feed
categoriesarray or stringnoCategory names, slugs, IDs, or objects
tagsarray or stringnoTag names, slugs, IDs, or objects
fieldsobjectnoCustom 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:

FieldTypeRequiredDescription
imagefileyesImage file
destinationstringnolocal (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 "image=@photo.jpg" \
  -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:

ValueBehavior
localSaves to Typecho's local /usr/uploads/ directory
bunnyUploads to BunnyCDN storage zone
r2Uploads to Cloudflare R2 bucket
bothUploads 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

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

Plugin Configuration

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

Authentication

SettingTypeDefaultDescription
authModeradiobothbasic, bearer, or both
allowedRolestextadministrator,editorComma-separated allowed user roles
bearerTokenstextarealabel:secret pairs, one per line
allowedIpstextareaIPv4/IPv6 addresses, one per line
defaultAuthorIdtext1Default author for bearer token requests

Cloud Storage

SettingTypeDefaultDescription
storageDestinationradiolocalDefault upload destination
bunnyAccessKeytextBunnyCDN API password
bunnyStorageZonetextBunnyCDN storage zone name
bunnyStorageHosttexthttps://syd.storage.bunnycdn.comBunnyCDN region endpoint
bunnyPublicHosttextBunnyCDN public URL prefix
r2AccessKeyIdtextCloudflare R2 access key ID
r2AccessKeySecrettextCloudflare R2 secret key
r2BuckettextR2 bucket name
r2EndpointtextR2 S3-compatible endpoint
r2PublicUrltextR2 public URL prefix
r2RootFoldertextOptional path prefix inside bucket

Quick Reference

ActionMethodEndpointAuth Required
List postsGET/typecho-api/v1/postsYes
Get postGET/typecho-api/v1/post?id=NYes
Create postPOST/typecho-api/v1/postsYes
Replace postPUT/typecho-api/v1/post?id=NYes
Partial updatePATCH/typecho-api/v1/post?id=NYes
Delete postDELETE/typecho-api/v1/post?id=NYes
Upload imagePOST/typecho-api/v1/uploadYes
Increment agreePOST/typecho-api/v1/agreeYes