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": "",
"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 |