v1

API Documentation

Everything Mastodon client developers need to integrate with ReadSync.

Bearer token auth JSON responses 3 endpoints Multi-account support

👋 You're a Mastodon client developer

This documentation explains how to authenticate and call the ReadSync API to store and retrieve read positions on behalf of your users.

Not a developer?

If you're a Mastodon user and your client already supports ReadSync, you just need to create an account and generate a token in your dashboard. Get started here.

Introduction

The ReadSync API lets Mastodon clients read and write a user's last-read timeline position. Any client that integrates with ReadSync can resume exactly where the user left off — on any device.

All API endpoints are under the base path:

https://your-instance.example.com/api/v1

Replace your-instance.example.com with the domain of the ReadSync instance the user registered on. Users self-host or use a shared instance.

Authentication

Every request must include a Bearer token in the Authorization header. Personal Access Tokens are generated from the user's dashboard under Settings → Access Tokens.

GET /api/v1/read_position HTTP/1.1
Host: your-instance.example.com
Authorization: Bearer rs_live_xxxxxxxxxxxxxxxxxxxx
Note: Tokens are shown only once at creation. Direct your users to Settings → Access Tokens to generate and copy their token. Tokens can be named (e.g. "Ivory on iPhone") and revoked independently.

Rate limiting

Requests are rate-limited per API key and per IP address.

60 req / minPer token
100 req / minPer IP address

When a limit is exceeded the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.

{
  "error": {
    "code": "too_many_requests",
    "detail": "Rate limit exceeded. Retry after 30 seconds."
  }
}

Errors

All error responses share the same envelope:

{
  "error": {
    "code": "unauthorized",
    "detail": "Invalid or revoked token"
  }
}
Status Code Meaning
401unauthorizedMissing, invalid, or revoked token
404not_foundResource not found (no position set yet, or unknown handle)
422unprocessable_entityMissing or invalid request parameter
429too_many_requestsRate limit exceeded
GET /api/v1/read_position

Get last-read position

Returns the last-read toot ID for the authenticated user's Mastodon account. If the user has multiple linked accounts, the first is returned by default. Use the handle parameter to target a specific account.

Query parameters

ParameterTypeRequiredDescription
handlestringoptionalFull Mastodon handle. Accepted formats: @user@server.example or user@server.example

Example request

GET /api/v1/read_position HTTP/1.1
Host: your-instance.example.com
Authorization: Bearer rs_live_xxxxxxxxxxxxxxxxxxxx

Example request with handle

GET /api/v1/read_position?handle=@you@mastodon.social HTTP/1.1
Host: your-instance.example.com
Authorization: Bearer rs_live_xxxxxxxxxxxxxxxxxxxx

Response — 200 OK

{
  "data": {
    "last_read_id": "114262948242478592",
    "mastodon_account": "@you@mastodon.social",
    "updated_at": "2026-04-27T14:00:00Z"
  },
  "meta": { "version": "v1" }
}

Response — 404 Not Found

Returned when no position has been written yet for this account.

{
  "error": {
    "code": "not_found",
    "detail": "No read position found for this account"
  }
}
PUT /api/v1/read_position

Update last-read position

Sets (or overwrites) the last-read toot ID. This is the primary write endpoint — call it whenever the user advances in their timeline. Use the handle query parameter to target a specific Mastodon account when the user has more than one linked.

Query parameters

ParameterTypeRequiredDescription
handlestringoptionalFull Mastodon handle to target. Defaults to the user's first linked account.

Request body

Content-Type: application/json

ParameterTypeRequiredDescription
last_read_idstringrequiredMastodon snowflake ID of the last toot the user has seen.

Example request

PUT /api/v1/read_position HTTP/1.1
Host: your-instance.example.com
Authorization: Bearer rs_live_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "last_read_id": "114262948242478592"
}

Response — 200 OK

{
  "data": {
    "last_read_id": "114262948242478592",
    "mastodon_account": "@you@mastodon.social",
    "updated_at": "2026-04-29T09:45:00Z"
  },
  "meta": { "version": "v1" }
}

Response — 422 Unprocessable Entity

Returned when last_read_id is missing.

{
  "error": {
    "code": "unprocessable_entity",
    "detail": "param is missing or the value is empty: last_read_id"
  }
}
GET /api/v1/mastodon_accounts

List Mastodon accounts

Returns all Mastodon accounts linked to the authenticated ReadSync user, including their current read positions. Use this to let users pick which account to sync, or to display all positions in your UI. Results are sorted by server then username.

Example request

GET /api/v1/mastodon_accounts HTTP/1.1
Host: your-instance.example.com
Authorization: Bearer rs_live_xxxxxxxxxxxxxxxxxxxx

Response — 200 OK

{
  "data": [
    {
      "handle": "@you@mastodon.social",
      "username": "you",
      "server": "mastodon.social",
      "verified": true,
      "last_read_id": "114262948242478592",
      "last_read_at": "2026-04-27T14:00:00Z"
    },
    {
      "handle": "@you@fosstodon.org",
      "username": "you",
      "server": "fosstodon.org",
      "verified": false,
      "last_read_id": null,
      "last_read_at": null
    }
  ],
  "meta": {
    "total": 2,
    "version": "v1"
  }
}

ReadPosition object

Field Type Description
last_read_idstringMastodon snowflake ID of the last toot read
mastodon_accountstringFull Mastodon handle (e.g. @you@mastodon.social)
updated_atstring (ISO 8601)When the position was last updated

MastodonAccount object

Field Type Description
handlestringFull Mastodon handle (e.g. @you@mastodon.social)
usernamestringUsername without server
serverstringMastodon instance domain
verifiedbooleanWhether the access token has been verified
last_read_idstring | nullLast-read toot ID, or null if none set
last_read_atstring | null (ISO 8601)When the position was last updated, or null