Files
MyActivityPub/docs/API.md
2026-01-24 17:45:29 +01:00

9.3 KiB

API Documentation

Overview

This document describes the Mastodon API integration used in My ActivityPub app. The app communicates with Mastodon-compatible servers using their REST API v1.

Base URL

Default: https://mastodon.social/

You can configure this to any Mastodon or ActivityPub-compatible instance.

Authentication

Currently, the app accesses public endpoints that don't require authentication. Future versions will implement OAuth 2.0 for authenticated requests.

Endpoints

1. Get Public Timeline

Retrieve statuses from the public timeline.

Endpoint: GET /api/v1/timelines/public

Parameters:

  • limit (integer, optional): Maximum number of results. Default: 20
  • local (boolean, optional): Show only local statuses. Default: false
  • max_id (string, optional): Return results older than this ID
  • since_id (string, optional): Return results newer than this ID
  • min_id (string, optional): Return results immediately newer than this ID

Example Request:

GET https://mastodon.social/api/v1/timelines/public?limit=20&local=false

Response: Array of Status objects

Example Response:

[
  {
    "id": "109382159165398564",
    "created_at": "2022-11-23T07:49:01.940Z",
    "content": "<p>Hello world!</p>",
    "account": {
      "id": "109382",
      "username": "alice",
      "display_name": "Alice",
      "avatar": "https://...",
      ...
    },
    "media_attachments": [],
    "favourites_count": 5,
    "reblogs_count": 2,
    "replies_count": 1,
    ...
  }
]

2. Get Instance Information

Get information about the Mastodon instance.

Endpoint: GET /api/v1/instance

Parameters: None

Example Request:

GET https://mastodon.social/api/v1/instance

Response: Instance object

Example Response:

{
  "uri": "mastodon.social",
  "title": "Mastodon",
  "short_description": "The original server operated by the Mastodon gGmbH non-profit",
  "description": "...",
  "version": "4.0.0",
  "languages": ["en"],
  "thumbnail": "https://..."
}

3. Get Account

Get account information.

Endpoint: GET /api/v1/accounts/:id

Parameters:

  • id (string, required): Account ID

Example Request:

GET https://mastodon.social/api/v1/accounts/109382

Response: Account object

Example Response:

{
  "id": "109382",
  "username": "alice",
  "acct": "alice",
  "display_name": "Alice",
  "locked": false,
  "bot": false,
  "created_at": "2022-11-23T07:49:01.940Z",
  "note": "<p>Bio goes here</p>",
  "url": "https://mastodon.social/@alice",
  "avatar": "https://...",
  "header": "https://...",
  "followers_count": 100,
  "following_count": 50,
  "statuses_count": 500
}

4. Get Account Statuses

Get statuses posted by an account.

Endpoint: GET /api/v1/accounts/:id/statuses

Parameters:

  • id (string, required): Account ID
  • limit (integer, optional): Maximum number of results. Default: 20
  • max_id (string, optional): Return results older than this ID
  • since_id (string, optional): Return results newer than this ID
  • exclude_replies (boolean, optional): Skip statuses that reply to other statuses
  • exclude_reblogs (boolean, optional): Skip statuses that are reblogs of other statuses
  • only_media (boolean, optional): Show only statuses with media attached

Example Request:

GET https://mastodon.social/api/v1/accounts/109382/statuses?limit=20

Response: Array of Status objects

Data Models

Status

Represents a post/toot on Mastodon.

Properties:

{
  id: string                    // Unique identifier
  created_at: string           // ISO 8601 datetime
  content: string              // HTML content
  account: Account             // Account that posted this status
  media_attachments: Array     // Media attachments
  reblog: Status | null        // If this is a reblog, the original status
  favourites_count: number     // Number of favorites
  reblogs_count: number        // Number of reblogs/boosts
  replies_count: number        // Number of replies
  favourited: boolean          // Has the current user favorited this?
  reblogged: boolean           // Has the current user reblogged this?
  url: string | null           // URL to the status
  visibility: string           // Visibility level (public, unlisted, private, direct)
}

Account

Represents a user account.

Properties:

{
  id: string                    // Unique identifier
  username: string             // Username (without @domain)
  acct: string                 // Full username (@username@domain)
  display_name: string         // Display name
  avatar: string               // URL to avatar image
  header: string               // URL to header image
  note: string                 // Bio/description (HTML)
  url: string | null           // URL to profile page
  followers_count: number      // Number of followers
  following_count: number      // Number of accounts being followed
  statuses_count: number       // Number of statuses posted
  bot: boolean                 // Is this a bot account?
  locked: boolean              // Does this account require follow requests?
}

MediaAttachment

Represents media files attached to statuses.

Properties:

{
  id: string                    // Unique identifier
  type: string                 // Type: image, video, gifv, audio, unknown
  url: string                  // URL to the media file
  preview_url: string | null   // URL to the preview/thumbnail
  remote_url: string | null    // Remote URL if the media is from another server
  description: string | null   // Alt text description
}

Instance

Represents a Mastodon instance.

Properties:

{
  uri: string                   // Domain name
  title: string                // Instance name
  description: string          // Long description (HTML)
  short_description: string    // Short description (plaintext)
  version: string              // Mastodon version
  thumbnail: string | null     // URL to thumbnail image
  languages: Array<string>     // ISO 639 Part 1-5 language codes
  email: string | null         // Contact email
  contact_account: Account     // Contact account
}

Error Handling

The API returns standard HTTP status codes:

  • 200 OK - Request succeeded
  • 400 Bad Request - Invalid parameters
  • 401 Unauthorized - Authentication required
  • 403 Forbidden - Access denied
  • 404 Not Found - Resource not found
  • 429 Too Many Requests - Rate limit exceeded
  • 500 Internal Server Error - Server error
  • 503 Service Unavailable - Server temporarily unavailable

Error Response Format:

{
  "error": "Error message here"
}

Rate Limiting

Mastodon implements rate limiting to prevent abuse. Limits vary by instance but typically:

  • 300 requests per 5 minutes for authenticated requests
  • Lower limits for unauthenticated requests

Rate limit information is returned in response headers:

  • X-RateLimit-Limit - Maximum number of requests
  • X-RateLimit-Remaining - Remaining requests in current window
  • X-RateLimit-Reset - Time when the limit resets (ISO 8601)

Best Practices

  1. Respect Rate Limits: Implement exponential backoff when hitting rate limits
  2. Cache Responses: Cache instance info and other static data
  3. Use Pagination: Use max_id and since_id for efficient pagination
  4. Handle Errors: Always handle network errors and API errors gracefully
  5. Validate Input: Validate user input before making API calls
  6. Use HTTPS: Always use HTTPS for API requests
  7. Set User-Agent: Include a descriptive User-Agent header

Implementation in the App

Service Layer

MastodonApiService.kt defines the API interface using Retrofit annotations:

interface MastodonApiService {
    @GET("api/v1/timelines/public")
    suspend fun getPublicTimeline(
        @Query("limit") limit: Int = 20,
        @Query("local") local: Boolean = false
    ): Response<List<Status>>
    
    // Other endpoints...
}

Repository Layer

MastodonRepository.kt wraps API calls with error handling:

suspend fun getPublicTimeline(limit: Int = 20, local: Boolean = false): Result<List<Status>> {
    return withContext(Dispatchers.IO) {
        try {
            val response = apiService.getPublicTimeline(limit, local)
            if (response.isSuccessful) {
                Result.success(response.body() ?: emptyList())
            } else {
                Result.failure(Exception("Error: ${response.code()}"))
            }
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

ViewModel Layer

TimelineViewModel.kt manages UI state and calls repository methods:

fun loadTimeline() {
    viewModelScope.launch {
        _uiState.value = TimelineUiState.Loading
        repository.getPublicTimeline().fold(
            onSuccess = { statuses ->
                _uiState.value = TimelineUiState.Success(statuses)
            },
            onFailure = { error ->
                _uiState.value = TimelineUiState.Error(error.message ?: "Unknown error")
            }
        )
    }
}

Further Reading