# Mastodon Scraper · Posts, Accounts, Hashtags & Instances (`reapx/mastodon-scraper`) Actor

Mastodon scraper for fediverse posts, profiles, hashtags, and instance timelines. Extract public Mastodon statuses, user accounts, engagement metrics, media, and outbound links with no login required.

- **URL**: https://apify.com/reapx/mastodon-scraper.md
- **Developed by:** [Tarek Etman](https://apify.com/reapx) (community)
- **Categories:** Social media, News, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 post returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Mastodon Scraper · Posts, Accounts, Hashtags & Instances

**Mastodon scraper** to scrape public posts, profiles, hashtags, and account feeds from any Mastodon fediverse instance (such as mastodon.social). Returns clean structured data per post: author details, follower counts, post text, engagement metrics, media, and outbound links.

No API key, token, app registration, or login required. This Mastodon scraper reads Mastodon's public REST API across any instance.

### What you get per post

| Field | |
|---|---|
| `handle` | the author as `instance/username` - unique across the whole fediverse |
| `accountAcct` | the same author in Mastodon's native `user@instance` form |
| `accountDisplayName` · `accountFollowers` · `accountStatusCount` · `accountIsBot` | who posted it |
| `content` · `spoilerText` · `language` · `visibility` · `isSensitive` | the post |
| `favouritesCount` · `reblogsCount` · `repliesCount` | engagement as the reading instance sees it |
| `hashtags` · `mentionCount` · `mediaCount` · `mediaTypes` | what is in it |
| `linkUrl` · `linkTitle` | where it points |
| `instanceDomain` · `sourceInstance` · `statusUrl` · `createdAt` · `retrievedAt` | |

### A hashtag timeline is federated

Reading `#rustlang` on one instance returns posts from across the fediverse, not only from
that instance - a hashtag timeline is the cheapest wide view Mastodon offers. Set **Only
posts from this instance** if you want the narrow one instead.

### Track a hashtag over time

Put your hashtags in, set **Maximum posts** high, and schedule the run. Every row carries
`statusUrl` and `createdAt`, so consecutive runs append cleanly and `handle` groups the
whole set by author.

### Watch an instance's own community

Turn on **Include the instance's public timeline** with **Only posts from this instance**
and point it at `fosstodon.org`, `hachyderm.io` or any instance you care about. Note that
some instances - `mastodon.social` among them - require a logged-in user for their public
timeline; those are skipped with a note in the run status rather than failing the run, and
their hashtag and trending timelines still work.

### Follow specific accounts

Put `user@instance` addresses in **Accounts**. Each is resolved on the instance you are
reading from and its public posts returned, with replies and boosts filtered out if you
want only original material.

### Input configuration

Every field is optional. The prefilled values below are what runs if you press Start without changing anything.

#### Where to read

An instance's hashtag timeline is federated, so one instance sees posts from across the fediverse.

| Field | Type | Accepts | What it does |
| --- | --- | --- | --- |
| `instances` | list | one value per line | Mastodon instance domains to read, one per line - mastodon.social, fosstodon.org, hachyderm.io, mstdn.social. Every source below is read on each instance and the results merged. Prefilled with `["mastodon.social"]`. |

#### What to read

Give hashtags, accounts, the instance timeline, trending - or any combination.

| Field | Type | Accepts | What it does |
| --- | --- | --- | --- |
| `hashtags` | list | one value per line | Hashtags to read, one per line, with or without the #. Each is fetched as its own timeline. Prefilled with `["photography"]`. |
| `accounts` | list | one value per line | Accounts whose public posts to read, one per line, as user@instance or a bare username for a local account. |
| `includePublicTimeline` | true/false | checkbox | Read each instance's public timeline. Some instances, mastodon.social among them, require a logged-in user for this and are skipped with a note rather than failing the run. |
| `includeTrending` | true/false | checkbox | Read what each instance is currently amplifying through its trends endpoint. |

#### Filters

All optional.

| Field | Type | Accepts | What it does |
| --- | --- | --- | --- |
| `localOnly` | true/false | checkbox | Restrict hashtag and public timelines to posts written on the instance itself, excluding federated posts from elsewhere. |
| `onlyWithMedia` | true/false | checkbox | Keep only posts carrying at least one image, video or audio attachment. |
| `excludeReplies` | true/false | checkbox | Drop posts that reply to another post. |
| `excludeReblogs` | true/false | checkbox | Drop boosts (reblogs) and keep only original posts. |
| `language` | string | free text | Keep only posts the author tagged with this two-letter language code. |
| `minFavourites` | integer | 0 to 1000000 | Keep only posts with at least this many favourites - the simplest way to skim the top of a busy hashtag. |

#### Output

How many rows you get and how politely they are fetched.

| Field | Type | Accepts | What it does |
| --- | --- | --- | --- |
| `maxPosts` | integer | 1 to 5000 | Hard ceiling on the rows this run produces, and therefore on what it costs. You are charged per post returned. Prefilled with `100`. |
| `requestsPerSecond` | integer | 1 to 5 | Politeness against each instance. Mastodon instances are volunteer-run; keep this low. Prefilled with `2`. |

### Pricing

Pay per event. The primary event is **Post returned** (`post-returned`), charged at **$0.001 per event** on the free plan, falling to $0.0005 on the highest tier.

One public Mastodon post delivered as a complete row, including its author, engagement counts, hashtags and media. Blocked instances and empty result sets are never charged.

Other charged events: `apify-actor-start` (Actor Start).

Minimum charge cap per run: $0.05.

Tiered discounts apply automatically on every paid Apify plan. Platform usage is absorbed by the Actor, so the per-event price is the whole price.

### Usage examples

#### The prefilled run, which is what Start does with nothing changed

```json
{
  "instances": [
    "mastodon.social"
  ],
  "hashtags": [
    "photography"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2
}
```

#### Filter by instances

```json
{
  "instances": [
    "mastodon.social",
    "fosstodon.org"
  ],
  "hashtags": [
    "photography"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2
}
```

#### Filter by hashtags

```json
{
  "instances": [
    "mastodon.social"
  ],
  "hashtags": [
    "opensource",
    "rustlang"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2
}
```

#### Filter by accounts

```json
{
  "instances": [
    "mastodon.social"
  ],
  "hashtags": [
    "photography"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2,
  "accounts": [
    "Gargron@mastodon.social",
    "torproject@mastodon.social"
  ]
}
```

#### Filter by include the instance's public timeline

```json
{
  "instances": [
    "mastodon.social"
  ],
  "hashtags": [
    "photography"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2,
  "includePublicTimeline": true
}
```

### Output example

One row, exactly as the actor wrote it to the dataset:

```json
{
  "handle": "mastodon.social/QasimRashid",
  "accountAcct": "QasimRashid@mastodon.social",
  "accountDisplayName": "Qasim Rashid, Esq.",
  "accountUrl": "https://mastodon.social/@QasimRashid",
  "accountFollowers": 92218,
  "accountFollowing": 106,
  "accountStatusCount": 4537,
  "accountIsBot": false,
  "accountCreatedAt": "2022-10-30T00:00:00.000Z",
  "instanceDomain": "mastodon.social",
  "sourceInstance": "fosstodon.org",
  "statusId": "117029450607940916",
  "statusUrl": "https://mastodon.social/@QasimRashid/117029450548542124",
  "content": "Hey so the Trump regime unilaterally forced Haitian Americans into undocumented status, and is now using that undocumented status as an excuse to force them to wear ankle monitors. A reminder that the \"just follow the...",
  "spoilerText": null,
  "createdAt": "2026-08-03T03:28:20.000Z",
  "editedAt": null,
  "language": "en"
}
```

That row carries 32 fields in total; 18 are shown.

#### Fields on every row

| Field | Type | What it is |
| --- | --- | --- |
| `handle` | string | The author's globally unique handle as instance/username. Unique across the whole fediverse, where a bare username is not, and stable across runs. |
| `accountAcct` | string | The author in Mastodon's native user@instance form. |
| `accountDisplayName` | string | The name the author shows on their profile. |
| `accountUrl` | string | The author's public profile page. |
| `accountFollowers` | integer | How many accounts follow the author, as their home instance reports it. |
| `accountFollowing` | integer | How many accounts the author follows. |
| `accountStatusCount` | integer | How many posts the author has published in total. |
| `accountIsBot` | true/false | True when the account declares itself automated. |
| `accountCreatedAt` | string | When the author's account was created. |
| `instanceDomain` | string | The instance the author's account lives on. |
| `sourceInstance` | string | The instance this run read the post through. Differs from the author's instance whenever a post arrived by federation. |
| `statusId` | string | The post's id on the instance it was read from. |
| `statusUrl` | string | The post's canonical public address on its author's instance. |
| `content` | string | The post body reduced from HTML to readable text. |
| `spoilerText` | string | The content warning the author put in front of the post, if any. |
| `createdAt` | string | When the post was published, in UTC. |
| `editedAt` | string | When the post was last edited, if it was. |
| `language` | string | The two-letter language code the author tagged the post with. |
| `visibility` | string | How widely the author published the post. |
| `isSensitive` | true/false | True when the author flagged the post or its media as sensitive. |
| `isReply` | true/false | True when the post replies to another post. |
| `isReblog` | true/false | True when the post is a boost of someone else's post rather than original. |
| `repliesCount` | integer | Replies the post has received, as the reading instance sees them. |
| `reblogsCount` | integer | Boosts the post has received, as the reading instance sees them. |
| `favouritesCount` | integer | Favourites the post has received, as the reading instance sees them. |
| `hashtags` | list | Every hashtag the post carries. |
| `mentionCount` | integer | How many accounts the post mentions. |
| `mediaCount` | integer | How many images, videos or audio files the post carries. |
| `mediaTypes` | list | The distinct kinds of media attached to the post. |
| `linkUrl` | string | The URL of the link preview card the post generated, if it linked out. |
| `linkTitle` | string | The title of the link preview card. |
| `retrievedAt` | string | When this row was read, in UTC. |

3 named dataset views ship with it: **Posts**, **Accounts**, **Engagement**. They drive the Output tab in Console and the Output block on the Actor's `.md` page.

### FAQ

**Does it need a Mastodon account or an access token?** No. Every endpoint it uses is
public and unauthenticated.

**Does it read private or followers-only posts?** No. Only what the instance serves
publicly; `visibility` on every row says which it was.

**Why does `sourceInstance` differ from `instanceDomain`?** Because the post arrived by
federation: `instanceDomain` is where the author lives, `sourceInstance` is where this run
read it.

**Are engagement counts exact?** They are what the reading instance knows. Mastodon is
federated, so a post's favourite count can differ slightly between instances. Reading the
author's own instance gives the authoritative figure.

**What is the stable identifier?** `handle`. Instance and username together address one
account across the whole network, and it is the right key to join runs together over time.

***

Unofficial - not affiliated with Mastodon gGmbH or any instance operator. Collects public
data only. reapx. Contact reapxdev@proton.me.

### The full published archive

Every run of this Actor is published as a permanent, citable page on **[reapx.dev](https://reapx.dev/data/mastodon-scraper/)** —
one page per entity, built from the real rows of real runs, with the run IDs it came from named
on the page.

- **[mastodon scraper archive](https://reapx.dev/data/mastodon-scraper/)** — every entity this Actor has observed
- **[All reapx datasets](https://reapx.dev/data/)** — 20 sources, tens of thousands of pages
- **[llms.txt](https://reapx.dev/llms.txt)** · **[feed.json](https://reapx.dev/feed.json)** ·
  **[feed.xml](https://reapx.dev/feed.xml)** — the machine-readable index, for agents

Each archive page carries Dataset JSON-LD, a canonical URL and the identifiers used, so an agent
can resolve an entity without running anything. Nothing on those pages is estimated or modelled.

# Actor input Schema

## `instances` (type: `array`):

Mastodon instance domains to read, one per line - mastodon.social, fosstodon.org, hachyderm.io, mstdn.social. Every source below is read on each instance and the results merged.

## `hashtags` (type: `array`):

Hashtags to read, one per line, with or without the #. Each is fetched as its own timeline.

## `accounts` (type: `array`):

Accounts whose public posts to read, one per line, as user@instance or a bare username for a local account.

## `includePublicTimeline` (type: `boolean`):

Read each instance's public timeline. Some instances, mastodon.social among them, require a logged-in user for this and are skipped with a note rather than failing the run.

## `includeTrending` (type: `boolean`):

Read what each instance is currently amplifying through its trends endpoint.

## `localOnly` (type: `boolean`):

Restrict hashtag and public timelines to posts written on the instance itself, excluding federated posts from elsewhere.

## `onlyWithMedia` (type: `boolean`):

Keep only posts carrying at least one image, video or audio attachment.

## `excludeReplies` (type: `boolean`):

Drop posts that reply to another post.

## `excludeReblogs` (type: `boolean`):

Drop boosts (reblogs) and keep only original posts.

## `language` (type: `string`):

Keep only posts the author tagged with this two-letter language code.

## `minFavourites` (type: `integer`):

Keep only posts with at least this many favourites - the simplest way to skim the top of a busy hashtag.

## `maxPosts` (type: `integer`):

Hard ceiling on the rows this run produces, and therefore on what it costs. You are charged per post returned.

## `requestsPerSecond` (type: `integer`):

Politeness against each instance. Mastodon instances are volunteer-run; keep this low.

## Actor input object example

```json
{
  "instances": [
    "mastodon.social",
    "fosstodon.org"
  ],
  "hashtags": [
    "opensource",
    "rustlang"
  ],
  "accounts": [
    "Gargron@mastodon.social",
    "torproject@mastodon.social"
  ],
  "includePublicTimeline": true,
  "includeTrending": true,
  "localOnly": true,
  "onlyWithMedia": true,
  "excludeReplies": true,
  "excludeReblogs": true,
  "language": "en",
  "minFavourites": 5,
  "maxPosts": 500,
  "requestsPerSecond": 2
}
```

# Actor output Schema

## `overview` (type: `string`):

The overview view of this run's dataset.

## `accounts` (type: `string`):

The accounts view of this run's dataset.

## `engagement` (type: `string`):

The engagement view of this run's dataset.

## `results` (type: `string`):

Every record this run produced, with all fields and no view applied. Field-level titles, types, descriptions and examples are declared in the dataset schema.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "instances": [
        "mastodon.social"
    ],
    "hashtags": [
        "photography"
    ],
    "maxPosts": 100,
    "requestsPerSecond": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("reapx/mastodon-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "instances": ["mastodon.social"],
    "hashtags": ["photography"],
    "maxPosts": 100,
    "requestsPerSecond": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("reapx/mastodon-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "instances": [
    "mastodon.social"
  ],
  "hashtags": [
    "photography"
  ],
  "maxPosts": 100,
  "requestsPerSecond": 2
}' |
apify call reapx/mastodon-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=reapx/mastodon-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/THak4EY7AedXTpT3Y/builds/nARuN2iCiOKc24njT/openapi.json
