# Mastodon Scraper (`goat255/mastodon-scraper`) Actor

Scrape Mastodon (fediverse) posts, profiles, hashtags, and account search without a login. Pull a user's posts by handle, a hashtag timeline, the public timeline, or matching profiles. Works against any instance and walks pagination up to your chosen limit.

- **URL**: https://apify.com/goat255/mastodon-scraper.md
- **Developed by:** [Goutam Soni](https://apify.com/goat255) (community)
- **Categories:** Social media, Lead generation, News
- **Stats:** 13 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.40 / 1,000 posts

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/actors/running/actors-in-store.md#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

Scrape public Mastodon posts, profiles, hashtags, and account search from across the fediverse with no login and no API key required. Point it at any Mastodon instance and pull a user's posts by handle, a hashtag timeline, the public timeline, or matching profiles as clean structured JSON, CSV, or Excel.

### What it does

- **Mastodon user posts by handle.** Works with a bare username on your chosen instance, a full `user@host` for a remote account, or a profile link. Pulls recent posts (statuses, boosts, replies) with engagement counts.
- **Profile data.** Optionally emit a profile row per handle with display name, bio, follower count, following count, and post count.
- **Hashtag scraper.** Pull every post on any instance's public hashtag timeline.
- **Public timeline scraper.** Stream an instance's public timeline (where the server allows it).
- **Profile search.** Find matching public Mastodon accounts by keyword.
- **Automatic pagination.** Walks page after page up to the limit you set, deduplicates posts, and stops cleanly when a source is exhausted.
- **Clean normalized output.** Plain-text post bodies, parsed media URLs, hashtags, mentions, and engagement metrics in one stable schema across every mode.

No account, no password, no API key. Give it an instance plus handles, hashtags, or search terms and it returns structured rows.

### Use cases

- **Social media monitoring.** Track a brand, topic, or hashtag across the fediverse and watch engagement over time.
- **Lead generation and audience research.** Search profiles by keyword and export handles, bios, and follower counts for outreach lists.
- **Market and competitor research.** Collect posts from accounts in your niche and analyze what resonates by favourites and boosts.
- **News and trend tracking.** Stream a hashtag or public timeline to spot emerging stories and conversations early.
- **Dataset building.** Assemble labeled post and profile datasets for sentiment analysis, content classification, or LLM training.

### Input

| Field | Type | Description |
|---|---|---|
| `instance` | string | The Mastodon server to scrape from for hashtags, search, and bare usernames. Host or URL. Default `mastodon.social`. |
| `handles` | array | Usernames to pull posts from. Bare username, `user@host`, or a profile link. |
| `hashtags` | array | Hashtags to pull from the instance's public tag timeline. With or without the `#` prefix. |
| `searchQueries` | array | Keywords to find matching public profiles on the chosen instance. |
| `publicTimeline` | boolean | When on, also pull the instance's public timeline. |
| `maxItemsPerSource` | integer | Cap per handle, hashtag, search, or timeline. Pagination is walked to reach it. Default `100`. |
| `includeReplies` | boolean | Include a handle's reply posts, not just top-level posts. Default `false`. |
| `includeReblogs` | boolean | Include posts a handle has boosted. Default `true`. |
| `onlyMedia` | boolean | Restrict feeds and the public timeline to posts that carry an image or video. Default `false`. |
| `includeProfile` | boolean | Emit one profile row per handle before its posts. Default `true`. |
| `concurrency` | integer | How many sources to process in parallel. Default `5`. |
| `proxyConfig` | object | Optional proxy. The public endpoints do not require one. |

#### Example input

```json
{
  "instance": "mastodon.social",
  "handles": ["example_user", "example_user@example.social"],
  "hashtags": ["opensource"],
  "maxItemsPerSource": 200,
  "includeProfile": true,
  "includeReplies": false,
  "includeReblogs": true
}
```

### Output

Each post is one row. Fields are ordered identity first, then engagement metrics, then content and media:

```json
{
  "type": "post",
  "id": "100000000000000000",
  "url": "https://example.social/@example_user/100000000000000000",
  "authorHandle": "example_user@example.social",
  "authorDisplayName": "Example User",
  "authorUrl": "https://example.social/@example_user",
  "favouritesCount": 42,
  "reblogsCount": 7,
  "repliesCount": 3,
  "quotesCount": 1,
  "text": "An example post about open source.",
  "language": "en",
  "tags": ["opensource"],
  "mentions": ["another_user@example.social"],
  "images": ["https://example.com/image.jpg"],
  "videos": [],
  "links": ["https://example.com/"],
  "visibility": "public",
  "sensitive": false,
  "spoilerText": null,
  "isReply": false,
  "inReplyToId": null,
  "isReblog": false,
  "createdAt": "2026-06-01T12:00:00.000Z",
  "editedAt": null
}
```

When `includeProfile` is on (or from profile search), a profile row carries:

```json
{
  "type": "profile",
  "id": "1",
  "handle": "example_user@example.social",
  "url": "https://example.social/@example_user",
  "displayName": "Example User",
  "followersCount": 12000,
  "followingCount": 300,
  "statusesCount": 4500,
  "description": "Example bio.",
  "avatar": "https://example.com/avatar.png",
  "header": "https://example.com/header.png",
  "locked": false,
  "bot": false,
  "createdAt": "2020-01-01T00:00:00.000Z"
}
```

#### Key fields

- **`type`** tells `post` rows apart from `profile` rows.
- **`url`** is the canonical link to the post or profile.
- **`favouritesCount`, `reblogsCount`, `repliesCount`, `quotesCount`** are the engagement metrics at scrape time.
- **`text`** is the post body as clean plain text. The original HTML is kept in `contentHtml`.
- **`images`, `videos`, `links`** are arrays of media and card URLs (empty when the post has none).
- **`isReblog`** marks a boosted post; **`isReply` / `inReplyToId`** mark replies.

### FAQ

**Is this scraper free? How is it priced?**
You pay only for what you run. There is no per-run start fee. See the pricing tab on this listing for the current rate.

**Do I need a Mastodon account, login, or API key?**
No. The scraper reads public data through the open fediverse endpoints. No account, password, or token is needed.

**How many posts or profiles can I get?**
Set `maxItemsPerSource` to any value up to 50000. The scraper walks pagination across as many pages as needed to reach your target or until the source runs out. Profile search returns up to the server's search limit.

**Which Mastodon instances does it support?**
Any instance. Set `instance` to the host you want (for example `mastodon.social` or `https://example.social`), and use `user@host` handles to reach accounts on other servers.

**Can I scrape a hashtag across the whole fediverse?**
You scrape a hashtag from the chosen instance's public tag timeline, which federates posts that instance has seen. Point at a larger, well-connected instance for broader coverage.

**How fast is it?**
Multiple sources run in parallel (set with `concurrency`), and each source pages until your limit. A typical handle or hashtag of a few hundred posts completes in seconds.

**What if a server blocks the public timeline or search?**
Some servers gate those endpoints. When one does, the run skips that mode and continues with your other handles, hashtags, and searches.

To improve our actors we collect anonymized usage telemetry (run stats and input patterns). No personal account data is collected.

# Actor input Schema

## `instance` (type: `string`):

The Mastodon server to scrape from for hashtags, search, and bare usernames. A host name or URL. Example: mastodon.social, https://fosstodon.org.

## `handles` (type: `array`):

Usernames to pull recent posts from. Use a bare username for the chosen instance, a full user@host for a remote account, or a full profile link. Example: example\_user, example\_user@example.social, https://example.social/@example\_user.

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

Hashtags to pull from the chosen instance's public tag timeline. With or without the # prefix. Example: opensource, photography.

## `searchQueries` (type: `array`):

Keywords to search for matching public profiles on the chosen instance. Example: open source, journalism.

## `publicTimeline` (type: `boolean`):

When on, also pull the chosen instance's public timeline. Some servers restrict this; if so the run continues with the other modes.

## `maxItemsPerSource` (type: `integer`):

Cap on items returned per handle, hashtag, search, or timeline. Pagination is walked across multiple pages until this is reached or the source is exhausted.

## `includeReplies` (type: `boolean`):

When on, a handle's reply posts are included alongside its top-level posts.

## `includeReblogs` (type: `boolean`):

When on, posts a handle has boosted are included alongside their own posts.

## `onlyMedia` (type: `boolean`):

When on, restrict user feeds and the public timeline to posts that carry an image or video.

## `includeProfile` (type: `boolean`):

When on, each handle also emits one profile record (display name, bio, follower counts) before its posts.

## `concurrency` (type: `integer`):

How many sources to process in parallel.

## `proxyConfig` (type: `object`):

Optional proxy. The public read endpoints do not require a proxy, but you may route through one if you wish.

## Actor input object example

```json
{
  "instance": "mastodon.social",
  "handles": [
    "Gargron"
  ],
  "hashtags": [],
  "searchQueries": [],
  "publicTimeline": false,
  "maxItemsPerSource": 100,
  "includeReplies": false,
  "includeReblogs": true,
  "onlyMedia": false,
  "includeProfile": true,
  "concurrency": 5,
  "proxyConfig": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `posts` (type: `string`):

No description

# 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 = {
    "instance": "mastodon.social",
    "handles": [
        "Gargron"
    ],
    "hashtags": [],
    "searchQueries": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("goat255/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 = {
    "instance": "mastodon.social",
    "handles": ["Gargron"],
    "hashtags": [],
    "searchQueries": [],
}

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

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

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

```

## CLI example

```bash
echo '{
  "instance": "mastodon.social",
  "handles": [
    "Gargron"
  ],
  "hashtags": [],
  "searchQueries": []
}' |
apify call goat255/mastodon-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,goat255/mastodon-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Iovr2Hsalz5GdKSOc/builds/bbleFqhrVJWTe1853/openapi.json
