Reference
HTTP API
The JSON API under /api — authentication, posts, users, feeds, tags, lists, and notifications.
The backend serves a JSON API under /api. The web app is its first
consumer: the frontend reverse-proxies /api/* to the backend, so the browser
talks to one origin, there is no CORS configuration, and the session cookie flows
naturally.
Authentication
Session-based. Signing in sets an httpOnly cookie backed by a row in the
Postgres sessions table. Send that cookie with subsequent requests.
Conventions
- Cursor pagination. Listing endpoints accept a cursor and return the next
one. There is no
offset. - Rate limiting. Writes are throttled per signed-in user, or per IP when
anonymous; reads are not throttled by the general limiter. A limited request
gets
429withRetry-After. - Errors are JSON, always the same shape —
{"error": "…"}— with an appropriate status code. A request body that fails validation gets400and a message naming the field, for example{"error":"displayName: Invalid input: expected string, received number"}. A body that is not valid JSON gets400too, not500.
Health
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
{"status":"ok"} |
GET |
/version |
Name, version, federation flag |
These sit at the root, not under /api.
Auth — /api/auth
| Method | Path | Purpose |
|---|---|---|
POST |
/register |
Create an account |
POST |
/login |
Sign in, setting the session cookie |
POST |
/logout |
End the session |
GET |
/me |
The current user |
DELETE |
/me |
Delete the account |
POST |
/password/forgot |
Send a reset email |
POST |
/password/reset |
Complete a reset with a token |
POST |
/password/change |
Change password while signed in |
POST |
/email/verify |
Verify an address with a token |
POST |
/email/resend |
Resend the verification email |
Posts — /api/posts
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
List posts — the Global timeline, assembled with per-author diversity (see Reading and following) |
POST |
/ |
Create a post (draft, scheduled, or published) |
GET |
/drafts |
The current user’s drafts |
GET |
/mine?status= |
The current user’s posts in one state — draft, scheduled, or published |
GET |
/mine/counts |
How many the current user holds in each state |
GET |
/trending |
Trending posts |
GET |
/by/:username/:slug |
A single post by its public URL |
GET |
/:id |
A single post |
PATCH |
/:id |
Update a post |
DELETE |
/:id |
Delete a post |
POST / DELETE |
/:id/like |
Like / unlike |
POST / DELETE |
/:id/recommend |
Recommend / un-recommend (federates as Announce/Undo(Announce)) |
GET |
/:id/comments |
List comments |
POST |
/:id/comments |
Add a comment |
PATCH / DELETE |
/:id/comments/:commentId |
Edit / delete a comment |
POST / DELETE |
/:id/comments/:commentId/like |
Like / unlike a comment |
Feed, search, dashboard
| Method | Path | Purpose |
|---|---|---|
GET |
/api/feed |
The personalised feed |
GET |
/api/search |
Posts, accounts, and tags |
GET |
/api/dashboard |
Writer dashboard figures |
Users — /api/users
| Method | Path | Purpose |
|---|---|---|
PATCH |
/me |
Update the profile |
PATCH |
/me/privacy |
Update privacy settings |
POST |
/me/custom-section/preview |
Render the custom About Markdown |
POST / DELETE |
/me/avatar |
Upload / remove the avatar |
GET |
/me/follow-requests |
Pending follow requests |
POST |
/me/follow-requests/:id/approve |
Approve a request |
POST |
/me/follow-requests/:id/reject |
Reject a request |
GET |
/me/muted, /me/blocked |
Muted / blocked accounts |
DELETE |
/me/followers/:identifier |
Remove a follower |
GET |
/suggested |
Suggested accounts |
GET |
/:username |
A profile |
GET |
/:username/followers, /:username/following, /:username/posts |
Their collections |
GET |
/:username/recommendations |
Posts they’ve recommended |
POST / DELETE |
/:username/follow |
Follow / unfollow |
POST / DELETE |
/:username/mute |
Mute / unmute |
POST / DELETE |
/:username/block |
Block / unblock |
Remote actors — /api/remote
Federated equivalents, keyed by handle (user@domain):
| Method | Path | Purpose |
|---|---|---|
GET |
/users/:handle |
Resolve and fetch a remote profile |
GET |
/users/:handle/posts |
Their posts, as cached locally |
GET |
/users/:handle/recommendations |
Posts they’ve recommended (recorded from inbound Announce) |
POST / DELETE |
/users/:handle/follow |
Follow / unfollow |
POST / DELETE |
/users/:handle/mute |
Mute / unmute |
POST / DELETE |
/users/:handle/block |
Block / unblock |
Tags — /api/tags
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
List tags |
GET |
/following |
Tags the user follows |
GET |
/:slug |
One tag |
GET |
/:slug/posts |
Posts under a tag |
POST / DELETE |
/:slug/follow |
Follow / unfollow a tag |
Lists — /api/lists
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
The user’s lists |
POST |
/ |
Create a list |
GET |
/read-later |
The built-in read-later list |
GET |
/user/:username |
A user’s public lists |
GET |
/for-post/:postId |
Which of your lists hold this post |
GET |
/:id |
One list |
GET |
/:id/items |
Its posts |
PATCH / DELETE |
/:id |
Update / delete the list |
POST |
/:id/items |
Add a post |
DELETE |
/:id/items/:postId |
Remove a post |
Notifications — /api/notifications
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
List notifications |
GET |
/unread-count |
Unread badge count |
POST |
/read |
Mark all as read |
POST |
/:id/read |
Mark one as read |
Media — /api/uploads
| Method | Path | Purpose |
|---|---|---|
POST |
/ |
Upload a file |
GET |
/:file |
Serve an uploaded file |
GET |
/og/:id.jpg |
A JPEG copy of an upload, for link-preview crawlers |
/og/:id.jpg takes the id of an existing upload — whatever its stored format —
and returns it as a JPEG fitted inside 1200×630, generated on first request and
cached on disk. It exists because uploads are stored as WebP, which several
preview crawlers refuse, and because a share image has to be a format every one
of them reads. Unauthenticated by necessity: the caller is a crawler with no
session.
Share cards — /api/og
| Method | Path | Purpose |
|---|---|---|
GET |
/posts/:id.jpg |
The card drawn for a post that has no image of its own |
A 1200×630 JPEG carrying the post’s title, its author, and the instance —
generated on first request and cached on disk. What a link-preview crawler
fetches as og:image when the post has neither a chosen banner nor an image in
its body. Unauthenticated, like the media route above, and built from the same
visibility check a signed-out reader gets: a draft’s card is a 404, exactly
like the draft.
Answers 302 to the instance’s brand image when no card can be drawn — a
federated copy of someone else’s post, an untitled one, or a title in a script
the bundled font has no glyphs for. A redirect rather than a 404 because an
og:image that fails puts no picture on the card at all.
Its own prefix rather than a path under /api/posts/ so robots.txt can allow
it by name — see Discoverability.
Nothing but share images belongs here.
Photos — /api/photos
Free-photo search for the editor’s banner picker. Signed in only — one provider spends an instance-wide quota.
| Method | Path | Purpose |
|---|---|---|
GET |
/providers |
Which providers this instance can search, in order |
GET |
/search?provider=&q=&page= |
Search one provider |
POST |
/use |
Tell a provider one of its photos was used |
/providers is never empty: Openverse needs no credentials, so it is always
present. Unsplash joins it only when an admin has configured a key. Results from
either provider come back in one shape, carrying the attribution to store with
the post.
/use exists for Unsplash’s API terms, which require a per-photo call when an
image is actually used; it is a no-op for providers that ask for nothing.
Search carries its own per-user rate limit on top of the general one, because a
GET is not covered by the write throttle and both providers are shared
resources.
Webhooks — /api/webhooks
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/content |
Token | Publish a document from an external CMS |
GET |
/tokens |
Session | Your live publishing tokens |
POST |
/tokens |
Session | Mint one (plaintext returned once) |
DELETE |
/tokens/:id |
Session | Revoke one |
/content is the one endpoint that does not use the session cookie: it
authenticates with a bearer credential in X-Webhook-Secret or
Authorization: Bearer, and publishes as that credential’s owner. Managing
tokens is an ordinary session-authenticated call, so a token cannot mint or
revoke tokens. Full reference in
Content webhook.
Reports, instance, setup, SEO
| Method | Path | Purpose |
|---|---|---|
POST |
/api/reports |
Report a post or actor |
GET |
/api/instance |
Public instance metadata |
GET |
/api/instance/tls-check |
Whether a hostname may be issued a certificate |
POST |
/api/setup |
Complete the first-run wizard |
POST |
/api/setup/test-email |
Send a test email during setup |
GET |
/api/seo |
Indexing flag and verification tokens (the IndexNow key is withheld) |
GET |
/api/seo/sitemap-entries |
Profiles, tags, and public lists for the sitemap, plus the post count |
GET |
/api/seo/sitemap-posts?page=N |
One page of published local posts for the sitemap |
GET |
/api/seo/indexnow-key/:key |
Whether key is this instance’s IndexNow key |
Administrative endpoints are documented separately in Admin API.
Found a mistake?Edit this page on GitHub.