Skip to content

API Reference

This is the complete REST API reference for Picora. Pick the endpoint family below.

For an interactive try-it-out experience powered by Scalar, open the API Explorer.

For the user-friendly task-oriented manual instead, see the User Guide.

Endpoint families

FamilyPath prefixAuthDocs
Authentication/v1/auth/*Public + JWT cookie(covered in user-guide security)
Images/v1/imagesBearer (JWT or API Key)Images API
Videos/v1/videosBearerVideos API
Audio/v1/audioBearerAudio API
Documents/v1/docsBearer(v0.15+ scheduled)
Unified media/v1/mediaBearer(combines images / videos / audio for listing + batch delete)
Upload/v1/images (multipart)BearerUpload guide
API Keys/v1/api-keysJWT only (cannot create keys with a key)(covered in user-guide api-keys)
Usage/v1/user/me/usageBearer(combined quota + bandwidth, see quota)
Billing/v1/billing/*JWT
Admin/v1/admin/*JWT + is_admin = 1(covered in admin guide)
Webhooks/webhooks/{provider}Provider-signed

Base URLs

PlatformAPI baseDefault media CDN
Overseas (CF)https://api.picora.mehttps://media.picora.me
Mainland (Aliyun)https://api.picora.cnhttps://media.picora.cn

The two platforms have fully isolated databases. An account on one is not accessible from the other. See FAQ → why two platforms?.

Authentication

Picora supports two authentication mechanisms (described in the user-facing API Keys guide and authorized OAuth apps).

API Key (Bearer sk_live_...)

Used by tool clients (PicGo / Moraya / your scripts) and stdio MCP. Set on every request:

Authorization: Bearer sk_live_abcdef0123456789...

JWT (web dashboard)

Browser sessions get JWT access tokens (15-min lifetime) + refresh token cookies (7-day, httpOnly). The dashboard handles refresh transparently. Direct API consumers should prefer API Keys, not JWTs.

OAuth 2.1 (HTTP MCP, v0.14+)

Hosted MCP server at mcp.picora.me uses OAuth 2.1 with refresh token rotation. See AI workflows → claude-desktop.

Common response shape

All Picora API endpoints return a uniform JSON envelope.

Success:

{ "success": true, "data": { ... } }

Error:

{ "success": false, "error": "Human-readable description", "code": "MACHINE_CODE", "meta": { ... } }

code is the machine-readable identifier (see errors). meta is endpoint-specific structured context (e.g., { "type": "img_storage", "used": 104857600, "limit": 104857600 } for QUOTA_EXCEEDED).

Pagination

List endpoints use cursor pagination (not offset). Pass the cursor from the previous response’s nextCursor field; an empty / absent cursor means the first page.

Terminal window
curl "https://api.picora.me/v1/images?limit=20"
# → { "items": [...], "nextCursor": "eyJpZCI6Il..." }
curl "https://api.picora.me/v1/images?limit=20&cursor=eyJpZCI6Il..."
# → { "items": [...], "nextCursor": null } // last page

Maximum limit per request is 50.

Rate limiting

Per-tier rate limits (introduced in v0.12.0):

TierEndpointsLimit (per minute)
readGET /v1/images, GET /v1/media, GET /v1/docs/*, GET /v1/admin/*600
mutationPATCH / DELETE operations on resources120
uploadPOST /v1/images, POST /v1/videos, POST /v1/audio, POST /v1/docs60

Excess requests return HTTP 429 with Retry-After header. See errors → RATE_LIMITED.

ID format

All resource IDs use nanoid (URL-safe). Two lengths:

ResourceLengthReason
Images11 chars (e.g., xK9mR2pQ7vB)Used in public CDN URLs; 11 chars balances brevity with collision resistance
Other (videos / audio / docs / users / API keys)21 chars (default nanoid)Internal IDs, more entropy

API keys use a special format: sk_live_ prefix + 32-char nanoid = 40 chars total.

Timestamps

All timestamps are returned in ISO 8601 UTC format:

{ "createdAt": "2026-04-27T08:00:00.000Z" }

Clients should format to user’s local timezone.

CORS

Browser requests from the picora-center dashboard work by default. API-Key-authenticated requests bypass Origin checks (this fix, v0.12.0+, lets Tauri-based desktop apps like Moraya call the API directly).

Versioning

The current API version is /v1/ and is stable. Breaking changes will introduce /v2/; the old version remains supported for at least 12 months after /v2/ ships.