# 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**: ```json [ { "id": "109382159165398564", "created_at": "2022-11-23T07:49:01.940Z", "content": "

Hello world!

", "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**: ```json { "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**: ```json { "id": "109382", "username": "alice", "acct": "alice", "display_name": "Alice", "locked": false, "bot": false, "created_at": "2022-11-23T07:49:01.940Z", "note": "

Bio goes here

", "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**: ```typescript { 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**: ```typescript { 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**: ```typescript { 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**: ```typescript { 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 // 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**: ```json { "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: ```kotlin interface MastodonApiService { @GET("api/v1/timelines/public") suspend fun getPublicTimeline( @Query("limit") limit: Int = 20, @Query("local") local: Boolean = false ): Response> // Other endpoints... } ``` ### Repository Layer `MastodonRepository.kt` wraps API calls with error handling: ```kotlin suspend fun getPublicTimeline(limit: Int = 20, local: Boolean = false): Result> { 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: ```kotlin 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 - [Official Mastodon API Documentation](https://docs.joinmastodon.org/api/) - [ActivityPub Specification](https://www.w3.org/TR/activitypub/) - [Retrofit Documentation](https://square.github.io/retrofit/) - [Kotlin Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html)