# Mastodon API

> Mastodon returns public profiles and posts as a workflow and API.

Get profile returns public account details from a handle or profile link.

- Page: https://fous.com/workflows/mastodon
- Handle: `@mastodon`
- Category: [Social](https://fous.com/workflows/category/social)
- Source website: https://mastodon.social
- Last verified: Sep 29, 2026
- Fous is not affiliated with Mastodon.

## Methods

### Get profile

Operation `get_profile`, version 1. 1 credit per call.

Get a public Mastodon profile by handle or profile link, including its bio, counts, dates and profile fields. Major servers are read directly; other servers require the account to be known to mastodon.social, and their home-server account ID is unavailable.

**Input**

| Field | Type | Required | Example | Description |
|---|---|---|---|---|
| `account` | string | yes | `"https://fosstodon.org/@kev"` | Full handle, handle on mastodon.social, or profile link, for example Gargron@mastodon.social. |

**Input schema**

```json
{
  "type": "object",
  "required": [
    "account"
  ],
  "properties": {
    "account": {
      "type": "string",
      "description": "Full handle, handle on mastodon.social, or profile link, for example Gargron@mastodon.social.",
      "examples": [
        "https://fosstodon.org/@kev",
        "Gargron@mastodon.social",
        "Mastodon"
      ]
    }
  },
  "additionalProperties": false,
  "examples": [
    {
      "account": "https://fosstodon.org/@kev"
    },
    {
      "account": "Gargron@mastodon.social"
    },
    {
      "account": "Mastodon"
    }
  ]
}
```

**Output**

| Field | Type | Example | Description |
|---|---|---|---|
| `bio` | string or null | `"Used to help run Fosstodon. These days, I just hang out here.\nI work in InfoSec and live with two kids, one wife, two ` | Bio in plain text. |
| `posts` | integer or null | `1322` | Number of posts. |
| `handle` | string | `"kev@fosstodon.org"` | Full account handle with server. |
| `is_bot` | boolean or null | `false` | Whether the account is marked as a bot. |
| `followers` | integer or null | `12990` | Follower count. |
| `following` | integer or null | `141` | Following count. |
| `account_id` | string or null | `"1"` | Account ID on the home server. |
| `avatar_url` | string or null | `"https://cdn.fosstodon.org/accounts/avatars/000/000/001/original/8f82f81f058d509e.png"` | Avatar image link. |
| `joined_date` | string or null | `"2017-08-01"` | Date the account joined its home server, YYYY-MM-DD. |
| `profile_url` | string | `"https://fosstodon.org/@kev"` | Canonical profile link. |
| `display_name` | string or null | `"Kev Quirk"` | The profile display name. |
| `profile_fields` | array |  | Profile labels and values in plain text. |
| `profile_fields[].label` | string | `"Blog"` |  |
| `profile_fields[].value` | string | `"https://kevquirk.com"` |  |
| `last_active_date` | string or null | `"2026-09-28"` | Date of the account’s last public post, YYYY-MM-DD, when available. |

**Example input**

```json
{
  "account": "https://fosstodon.org/@kev"
}
```

**Example output**

```json
{
  "bio": "Used to help run Fosstodon. These days, I just hang out here.\nI work in InfoSec and live with two kids, one wife, two dogs, a blind cat, a flock of 25 chickens, 8 goats, and more fish than I can count…",
  "posts": 1322,
  "handle": "kev@fosstodon.org",
  "is_bot": false,
  "followers": 12990,
  "following": 141,
  "account_id": "1",
  "avatar_url": "https://cdn.fosstodon.org/accounts/avatars/000/000/001/original/8f82f81f058d509e.png",
  "joined_date": "2017-08-01",
  "profile_url": "https://fosstodon.org/@kev",
  "display_name": "Kev Quirk",
  "profile_fields": [
    {
      "label": "Blog",
      "value": "https://kevquirk.com"
    },
    {
      "label": "Pure Commons",
      "value": "https://purecommons.org"
    }
  ],
  "last_active_date": "2026-09-28"
}
```

### List hashtag posts

Operation `list_hashtag_posts`, version 1. 1 credit per call.

List the latest public posts for a hashtag on any Mastodon server, newest first. Only posts visible to the selected server are included; some servers require login for public timelines.

**Input**

| Field | Type | Required | Example | Description |
|---|---|---|---|---|
| `server` | string | no | `"mastodon.social"` | Mastodon server to read, such as fosstodon.org. Defaults to mastodon.social. |
| `hashtag` | string | yes | `"climate"` | Hashtag, with or without #, such as climate. |
| `max_results` | integer | no | `55` | Maximum posts to return, such as 20; at most 100. |

**Input schema**

```json
{
  "type": "object",
  "required": [
    "hashtag"
  ],
  "properties": {
    "server": {
      "type": "string",
      "default": "mastodon.social",
      "description": "Mastodon server to read, such as fosstodon.org. Defaults to mastodon.social.",
      "examples": [
        "mastodon.social",
        "fosstodon.org"
      ]
    },
    "hashtag": {
      "type": "string",
      "minLength": 1,
      "description": "Hashtag, with or without #, such as climate.",
      "examples": [
        "climate",
        "photography",
        "linux"
      ]
    },
    "max_results": {
      "type": "integer",
      "default": 20,
      "maximum": 100,
      "minimum": 1,
      "description": "Maximum posts to return, such as 20; at most 100.",
      "x-fous-developer": true,
      "examples": [
        55,
        5,
        2
      ]
    }
  },
  "additionalProperties": false,
  "examples": [
    {
      "hashtag": "climate"
    },
    {
      "server": "mastodon.social",
      "hashtag": "photography",
      "max_results": 55
    },
    {
      "server": "fosstodon.org",
      "hashtag": "linux",
      "max_results": 5
    }
  ]
}
```

**Output**

| Field | Type | Example | Description |
|---|---|---|---|
| `posts` | array |  | Latest public posts for the hashtag, newest first. |
| `posts[].text` | string | `"The Lowery Organ is pumping out tunes 24/7 head to nitetime.net to listen\n#NitetimeNet #LiveOrgan #MIDI #OrganMusic #R` | Post text without markup. |
| `posts[].boosts` | integer | `2` | Number of boosts. |
| `posts[].post_id` | string | `"117352340958210867"` | Post ID on the selected server. |
| `posts[].replies` | integer | `0` | Number of replies. |
| `posts[].post_link` | string or null | `"https://mastodon.social/@danconderman/117352340925381168"` | Page link for this post. |
| `posts[].posted_at` | string | `"2026-09-29T04:03:36Z"` | Post time in UTC. |
| `posts[].favourites` | integer | `0` | Number of favourites. |
| `posts[].image_links` | array |  | Links to attached images. |
| `posts[].author_handle` | string | `"@danconderman@mastodon.social"` | Author handle including server. |
| `posts[].author_display_name` | string | `"Daniel Conderman"` | Author display name. |

**Example input**

```json
{
  "server": "fosstodon.org",
  "hashtag": "linux",
  "max_results": 5
}
```

**Example output**

```json
{
  "posts": [
    {
      "text": "The Lowery Organ is pumping out tunes 24/7 head to nitetime.net to listen\n#NitetimeNet #LiveOrgan #MIDI #OrganMusic #RetroComputing #OldWeb #IndieWeb #DIYTech #Broadcasting #OBS #Linux #GenerativeVisu…",
      "boosts": 2,
      "post_id": "117352340958210867",
      "replies": 0,
      "post_link": "https://mastodon.social/@danconderman/117352340925381168",
      "posted_at": "2026-09-29T04:03:36Z",
      "favourites": 0,
      "image_links": [
        "https://cdn.fosstodon.org/cache/media_attachments/files/117/352/342/268/155/512/original/94fb1a44c1f98825.png"
      ],
      "author_handle": "@danconderman@mastodon.social",
      "author_display_name": "Daniel Conderman"
    },
    {
      "text": "From the times of the Great Old Ones to today, enchanted devices evolve. #Linux #OpenSource https://cromwell-intl.com/open-source/rhel-oracle-centos-6-7-8-9/?s=mc",
      "boosts": 0,
      "post_id": "117352322949449458",
      "replies": 0,
      "post_link": "https://mstdn.social/@conansysadmin/117352322899483385",
      "posted_at": "2026-09-29T03:59:01Z",
      "favourites": 0,
      "image_links": [],
      "author_handle": "@conansysadmin@mstdn.social",
      "author_display_name": "Conan the Sysadmin"
    }
  ]
}
```

### List recent posts

Operation `list_recent_posts`, version 1. 1 credit per call.

List an account's latest public posts across Mastodon servers, newest first. Includes optional replies and boosts; private and unlisted posts are not shown.

**Input**

| Field | Type | Required | Example | Description |
|---|---|---|---|---|
| `account` | string | yes | `"https://mastodon.online/@Gargron"` | Full account handle including server, or a profile link, such as Gargron@mastodon.social. |
| `max_results` | integer | no | `7` | Maximum number of posts to return, for example 20. |
| `include_boosts` | boolean | no | `true` | Include boosts, for example true. |
| `include_replies` | boolean | no | `true` | Include replies, for example true. |

**Input schema**

```json
{
  "type": "object",
  "required": [
    "account"
  ],
  "properties": {
    "account": {
      "type": "string",
      "description": "Full account handle including server, or a profile link, such as Gargron@mastodon.social.",
      "examples": [
        "https://mastodon.online/@Gargron",
        "Gargron@mastodon.social",
        "Mastodon@mastodon.social"
      ]
    },
    "max_results": {
      "type": "integer",
      "default": 20,
      "maximum": 100,
      "minimum": 1,
      "description": "Maximum number of posts to return, for example 20.",
      "x-fous-developer": true,
      "examples": [
        7,
        45,
        5
      ]
    },
    "include_boosts": {
      "type": "boolean",
      "default": false,
      "description": "Include boosts, for example true.",
      "examples": [
        true
      ]
    },
    "include_replies": {
      "type": "boolean",
      "default": false,
      "description": "Include replies, for example true.",
      "examples": [
        true
      ]
    }
  },
  "additionalProperties": false,
  "examples": [
    {
      "account": "https://mastodon.online/@Gargron",
      "max_results": 7,
      "include_boosts": true,
      "include_replies": true
    },
    {
      "account": "Gargron@mastodon.social"
    },
    {
      "account": "Mastodon@mastodon.social",
      "max_results": 45
    }
  ]
}
```

**Output**

| Field | Type | Example | Description |
|---|---|---|---|
| `posts` | array |  |  |
| `posts[].text` | string | `"@shoq @gnomon It is more intense. I don't have much of a choice though!"` |  |
| `posts[].boosts` | integer | `0` |  |
| `posts[].post_id` | string | `"109251632999281181"` |  |
| `posts[].replies` | integer | `2` |  |
| `posts[].is_boost` | boolean | `false` |  |
| `posts[].post_link` | string | `"https://mastodon.online/@Gargron/109251632999281181"` |  |
| `posts[].posted_at` | string | `"2022-10-29T12:47:02Z"` |  |
| `posts[].favourites` | integer | `0` |  |
| `posts[].image_links` | array |  |  |
| `posts[].content_warning` | string or null |  |  |
| `posts[].original_author` | string or null | `"stux@mstdn.social"` |  |

**Example input**

```json
{
  "account": "https://mastodon.online/@Gargron",
  "max_results": 7,
  "include_boosts": true,
  "include_replies": true
}
```

**Example output**

```json
{
  "posts": [
    {
      "text": "@shoq @gnomon It is more intense. I don't have much of a choice though!",
      "boosts": 0,
      "post_id": "109251632999281181",
      "replies": 2,
      "is_boost": false,
      "post_link": "https://mastodon.online/@Gargron/109251632999281181",
      "posted_at": "2022-10-29T12:47:02Z",
      "favourites": 0,
      "image_links": [],
      "content_warning": null,
      "original_author": null
    },
    {
      "text": "This banana messed with the wrong #cat",
      "boosts": 11,
      "post_id": "108634533212714839",
      "replies": 3,
      "is_boost": true,
      "post_link": "https://mastodon.online/users/Gargron/statuses/108634533212714839/activity",
      "posted_at": "2022-07-12T13:10:27Z",
      "favourites": 6,
      "image_links": [],
      "content_warning": null,
      "original_author": "stux@mstdn.social"
    },
    {
      "text": "Sm0ll but angry",
      "boosts": 16,
      "post_id": "108628583720421744",
      "replies": 3,
      "is_boost": true,
      "post_link": "https://mastodon.online/users/Gargron/statuses/108628583720421744/activity",
      "posted_at": "2022-07-11T11:57:25Z",
      "favourites": 9,
      "image_links": [],
      "content_warning": null,
      "original_author": "stux@mstdn.social"
    }
  ]
}
```

## Quick start

Call the API with a Fous API key (`FOUS_API_KEY`). To create one, turn on Developer mode in Fous Studio, then open Keys & connections → API keys (https://app.fous.com/keys).

```bash
# First set your key: export FOUS_API_KEY='YOUR_FOUS_API_KEY'
: "${FOUS_API_KEY:?Set FOUS_API_KEY before running this example}"

curl 'https://api.fous.com/v1/query' \
  --fail-with-body --silent --show-error --max-time 120 \
  -H "Authorization: Bearer $FOUS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "api": "@mastodon",
  "visibility": "public",
  "operation": "get_profile",
  "version": 1,
  "input": {
    "account": "https://fosstodon.org/@kev"
  },
  "response": {
    "format": "json"
  }
}'
```

```python
# Save as fous.py and run with python3 fous.py. No packages needed.
# First set your key: export FOUS_API_KEY='YOUR_FOUS_API_KEY'
import json
import os
import urllib.error
import urllib.request

api_key = os.environ.get("FOUS_API_KEY")
if not api_key:
    raise RuntimeError("Set FOUS_API_KEY before running this example")

body = json.loads("{\n  \"api\": \"@mastodon\",\n  \"visibility\": \"public\",\n  \"operation\": \"get_profile\",\n  \"version\": 1,\n  \"input\": {\n    \"account\": \"https://fosstodon.org/@kev\"\n  },\n  \"response\": {\n    \"format\": \"json\"\n  }\n}")
request = urllib.request.Request(
    "https://api.fous.com/v1/query",
    data=json.dumps(body).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urllib.request.urlopen(request, timeout=120) as response:
        result = json.load(response)
except urllib.error.HTTPError as error:
    raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8', errors='replace')}") from error
if result.get("success") is False:
    raise RuntimeError(result.get("error", {}).get("message", "Request failed"))
print(json.dumps(result["data"]["output"], indent=2))
```

```typescript
// Save as fous.mts and run with npx tsx fous.mts.
// First set your key: export FOUS_API_KEY='YOUR_FOUS_API_KEY'
const apiKey = process.env.FOUS_API_KEY;
if (!apiKey) throw new Error("Set FOUS_API_KEY before running this example");

const response = await fetch("https://api.fous.com/v1/query", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  signal: AbortSignal.timeout(120_000),
  body: JSON.stringify({
  "api": "@mastodon",
  "visibility": "public",
  "operation": "get_profile",
  "version": 1,
  "input": {
    "account": "https://fosstodon.org/@kev"
  },
  "response": {
    "format": "json"
  }
}),
});
type ApiResult = { success: boolean; data?: { output: unknown }; error?: { message: string } };
const result: ApiResult = await response.json();
if (!response.ok || result.success === false) {
  throw new Error(result.error?.message ?? `HTTP ${response.status}`);
}
if (!result.data) throw new Error("Missing API response data");
console.log(result.data.output);
```

Or describe the data in plain language: send `{"api":"@mastodon","prompt":"Describe the data you need, with every detail"}` to the same URL. Fous fills in the input, runs the method that fits and returns only the fields you asked for; `data.route.calls[].request` is the exact call it made. Routing is free; the run costs the same.

## Use cases

- Review public profile details and follower counts
- Track an account’s latest public posts
- Monitor public posts for a hashtag
- Compare post engagement counts

## FAQ

### Is Fous affiliated with Mastodon?

No. Fous is not affiliated with Mastodon. This workflow reads the public mastodon.social website and returns its data.

### How much does it cost?

Each run costs 1 credit. With pay-as-you-go, a credit costs 1¢; monthly plans cost less per credit.

### Do I need a Mastodon account?

No. You only need a Fous account.

### How current is the data?

Fous gets the data from mastodon.social when you run it; repeating the same request within a day may return the saved result. Fous checks this workflow automatically; it last passed a check on Sep 29, 2026.

### What are an account’s latest public posts?

List recent posts returns an account’s latest public posts, newest first. Replies and boosts can be included.

### What public posts use a hashtag?

List hashtag posts returns the latest public posts for a hashtag, newest first, from the selected server.

### What details are on a public profile?

Get profile returns details such as the bio, post and follower counts, dates, and profile fields.

## Related

- [Instagram API](https://fous.com/workflows/instagram.md): Instagram provides public photos, videos, profiles, exact headline counts, bio links, and profile pictures, plus recent public posts and reels; some profiles or posts require login, and view counts may be unavailable.
- [Bluesky API](https://fous.com/workflows/bluesky.md): Bluesky provides public profile details and latest posts, plus searches of up to 100 matching public posts; search results may lag behind new posts.
- [Threads API](https://fous.com/workflows/threads.md): Threads provides public profiles and posts, including recent posts, engagement and replies; private or login-only content is excluded, and replies may be limited to 10.
- [X API](https://fous.com/workflows/x.md): X returns public profile details and public posts with author, media, and engagement; deleted, protected, or unavailable posts cannot be retrieved.
- [Telegram API](https://fous.com/workflows/telegram.md): Telegram provides public channel details and the latest posts visible in public previews, with media and view totals that may be rounded or approximate.
- [Tumblr API](https://fous.com/workflows/tumblr.md): Tumblr blogs and posts.
- [Substack API](https://fous.com/workflows/substack.md): Substack returns public newsletters and recent public post details or text; paid posts expose previews or metadata, and some custom-domain newsletters may be unavailable.
- [TikTok API](https://fous.com/workflows/tiktok.md): TikTok returns public profiles and totals, recent pinned-first videos, and public video details and engagement; private content unavailable, and feeds may be incomplete or empty.
- [All Social workflows](https://fous.com/workflows/category/social)
