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

Collect public posts and profiles from Mastodon and compatible servers. Get text, authors, engagement counts, media links, and instance details in a dataset for research and social listening.

- **URL**: https://apify.com/maximedupre/mastodon-scraper.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Social media, Marketing, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 🐘 Collect public Mastodon posts and profiles

Researchers, social media teams, and developers can use Mastodon Scraper to collect public posts and profiles from Mastodon and compatible servers. Get text, authors, engagement counts, media links, and community details in a dataset. Use the rows to study topics and accounts without copying each post by hand.

- Study posts about a topic with the **[Mastodon hashtag scraper](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-hashtag-scraper)**.
- Review an account's public posts with the **[Mastodon account scraper](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-account-scraper)**.
- Find public accounts for a research topic with **[Mastodon profile search](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-profile-search)**.
- Review an instance's public feed with the **[Mastodon public timeline scraper](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-public-timeline-scraper)**.
- See posts an instance lists as **[Mastodon trending posts](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-trending-posts)**.
- Collect posts with images or videos with the **[Mastodon media scraper](https://apify.com/maximedupre/mastodon-scraper/examples/mastodon-media-scraper)**.

#### 📦 Get post content, profiles, and community details

Each dataset row is a post or a profile. Post rows include readable text and original HTML, public links, author details, counts, tags, mentions, media, and dates when available. They can also include reply and boost context, quoted posts, polls, custom emojis, reactions, link cards, tagged collections, and quote policies.

Profile rows can include biographies, images, audience counts, declared profile fields, and account settings. Both row types can include the chosen instance's public descriptions, contact email, languages, software version, totals, rules, and published service limits. Missing optional fields are left out. Supplied zero counts, false flags, empty text, and empty arrays can remain.

The Actor saves each public URL once per run. The first eligible occurrence supplies its row; later matches do not add more data. For boosts, the row describes the original post and can include the account that boosted it.

#### ▶️ Choose an instance and a collection method

1. Enter the full instance URL.
2. Choose Posts or Profiles.
3. For Posts, choose account feeds, hashtag feeds, keyword search, public timeline, or trending posts. For Profiles, enter keywords.
4. Fill in the matching list and set a total result limit if needed.
5. Start the run, then open the dataset to review or export the rows.

No Mastodon login or source API key is needed. Results cover what the chosen instance makes public and has seen, rather than the whole fediverse. Public features differ by instance. Public posts on Pleroma, Akkoma, and other Mastodon-compatible servers are supported where the chosen feature is available.

#### ⚙️ Input

**Input fields**

| Field | Type | What it does |
|---|---|---|
| `instanceUrl` | string | Required full HTTP or HTTPS instance URL with no path or sign-in details. The form prefills `https://mastodon.social`; raw input has no default. |
| `maxItems` | integer | Optional total row limit across all submitted values. Minimum 0. Omit it or use 0 for all available rows. A positive value caps saved rows, not guaranteed matches. The form prefill is 20, not a default. |
| `resultType` | string | Required: `posts` or `profiles`. The form prefills `posts`; raw input has no default. Fields for the other type are ignored. |
| `postMethod` | string | For Posts: `accounts`, `hashtags`, `search`, `timeline`, or `trending`. Defaults to `hashtags` when omitted. Ignored for Profiles. |
| `accounts` | array of strings | Required non-empty list for account feeds. Use a bare username on the chosen instance, full `user@host` handle, or exact public profile URL. Ignored for other choices. No default. |
| `hashtags` | array of strings | Required non-empty list for hashtag feeds, with or without `#`. The form prefills `["science"]`; raw input has no default. Ignored for other choices. |
| `keywords` | array of strings | Required non-empty list for Profiles or post keyword search. Each word or phrase is a separate search. Keep a whole phrase in one entry. No default. Ignored for other choices. |
| `includeReplies` | boolean | Defaults to `true`. Set `false` to omit replies from account and hashtag feeds. Ignored for other choices. |
| `includeBoosts` | boolean | Defaults to `true`. Set `false` to omit boosts from account and hashtag feeds. Ignored for other choices. |
| `onlyMedia` | boolean | Defaults to `false`. Set `true` to keep only posts with images, videos, or animated video in account feeds and public timelines. Ignored for other choices. |

Timeline and trending choices need no search list. Active list entries must be non-empty strings. The same filters apply to every entry in a run. Use separate runs for different methods or filter sets. The schema sets no upper row limit; public access and available history still limit results.

**Example input**

This is the public input from a successful current-beta default-input run:

```json
{
  "instanceUrl": "https://mastodon.social",
  "maxItems": 20,
  "resultType": "posts",
  "postMethod": "hashtags",
  "hashtags": [
    "science"
  ],
  "includeReplies": true,
  "includeBoosts": true,
  "onlyMedia": false
}
```

#### 🧾 Output

**Output fields**

A row has one of the two shapes below. Dotted paths describe fields inside nested objects or array entries. Optional fields appear only when supplied. IDs belong to the instance that returned them; use public URLs to compare posts across instances.

**Post row**

`resultType` is `post`. The table includes all post fields. A quoted post may have only some details. Its missing details do not remove the parent post. Reaction account IDs and collection member IDs may be incomplete.

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Kind of result in this row. |
| `id` | string | Post ID on the Mastodon instance that returned it. |
| `url` | string | Public post link. |
| `text` | string | Readable post text. Media-only posts can have no text. |
| `contentHtml` | string | Formatted post content supplied by the source. |
| `author` | object | Public account identity. |
| `author.id` | string | Account ID on the Mastodon instance that returned it. |
| `author.handle` | string | Full account handle, with the user name and host. |
| `author.displayName` | string | Display name set by the account. |
| `author.url` | string | Public profile link. |
| `favouritesCount` | integer | Favourite count reported by the instance. |
| `boostsCount` | integer | Boost count reported by the instance. |
| `repliesCount` | integer | Reply count reported by the instance. |
| `quotesCount` | integer | Quote count reported by the instance, when supplied. |
| `hashtags` | array of strings | Hashtags stated on the post. |
| `mentions` | array of objects | Accounts mentioned in the post. |
| `mentions.id` | string | Account ID on the Mastodon instance that returned it. |
| `mentions.handle` | string | Full account handle, with the user name and host. |
| `mentions.displayName` | string | Display name set by the account. |
| `mentions.url` | string | Public profile link. |
| `links` | array of strings | Links in the post content. |
| `cardUrl` | string | Linked-page URL from the post card, when supplied. |
| `media` | array of objects | Post images, videos, and animated media with source file links. |
| `media.type` | string | Media type reported by the source. |
| `media.url` | string | URL to download the media file from its source. |
| `media.previewUrl` | string | Preview image URL, when supplied. |
| `media.id` | string | Source attachment ID. |
| `media.description` | string | Attachment accessibility text. |
| `media.original` | object | Original media dimensions and duration. |
| `media.original.width` | integer | Width in pixels. |
| `media.original.height` | integer | Height in pixels. |
| `media.original.duration` | number | Source-reported duration in seconds. |
| `media.preview` | object | Preview image dimensions. |
| `media.preview.width` | integer | Width in pixels. |
| `media.preview.height` | integer | Height in pixels. |
| `media.blurhash` | string | Blurhash text for a blurred preview. |
| `publishedAt` | string | Post publication date reported by the source. |
| `editedAt` | string | Post edit date reported by the source, when supplied. |
| `language` | string | Post language code reported by the source. |
| `visibility` | string | Post visibility reported by the source. |
| `sensitive` | boolean | Whether the source labels the post as sensitive. |
| `contentWarning` | string | Content warning supplied with the post. |
| `replyTo` | object | Parent post reference for a reply. |
| `replyTo.postId` | string | Parent post ID on the instance that returned it. |
| `replyTo.accountId` | string | Parent author account ID on that instance. |
| `boostedBy` | object | Account that boosted the post in the first feed item saved for it. |
| `boostedBy.id` | string | Account ID on the Mastodon instance that returned it. |
| `boostedBy.handle` | string | Full account handle, with the user name and host. |
| `boostedBy.displayName` | string | Display name set by the account. |
| `boostedBy.url` | string | Public profile link. |
| `application` | object | Source-stated publishing application. |
| `application.name` | string | Application name. |
| `application.website` | string | Application website. |
| `threadUri` | string | Source-provided thread or conversation URI. |
| `quoteState` | string | Quote state reported by the source, not a collection status. |
| `quotedPost` | object | Quoted public post supplied with this post. Missing details are not fetched. |
| `quotedPost.id` | string | Post ID on the Mastodon instance that returned it. |
| `quotedPost.url` | string | Public post link. |
| `quotedPost.text` | string | Readable post text. Media-only posts can have no text. |
| `quotedPost.contentHtml` | string | Formatted post content supplied by the source. |
| `quotedPost.author` | object | Public account identity. |
| `quotedPost.author.id` | string | Account ID on the Mastodon instance that returned it. |
| `quotedPost.author.handle` | string | Full account handle, with the user name and host. |
| `quotedPost.author.displayName` | string | Display name set by the account. |
| `quotedPost.author.url` | string | Public profile link. |
| `quotedPost.favouritesCount` | integer | Favourite count reported by the instance. |
| `quotedPost.boostsCount` | integer | Boost count reported by the instance. |
| `quotedPost.repliesCount` | integer | Reply count reported by the instance. |
| `quotedPost.quotesCount` | integer | Quote count reported by the instance, when supplied. |
| `quotedPost.publishedAt` | string | Post publication date reported by the source. |
| `quotedPost.editedAt` | string | Post edit date reported by the source, when supplied. |
| `poll` | object | Poll supplied with the post. |
| `poll.id` | string | Source poll ID. |
| `poll.expiresAt` | string | Poll closing time. |
| `poll.multiple` | boolean | Whether more than one choice is allowed. |
| `poll.votesCount` | integer | Total votes reported by the source. |
| `poll.votersCount` | integer | Total voters reported by the source. |
| `poll.options` | array of objects | Options in source order. |
| `poll.options.text` | string | Option text. |
| `poll.options.votesCount` | integer | Source-reported votes for this option. |
| `emojis` | array of objects | Custom emojis supplied with this content. |
| `emojis.shortcode` | string | Emoji text code. |
| `emojis.url` | string | URL to download the emoji image, which may be animated. |
| `emojis.staticUrl` | string | URL to download the still emoji image. |
| `emojiReactions` | array of objects | Source-reported emoji reactions. |
| `emojiReactions.name` | string | Reaction name or emoji. |
| `emojiReactions.count` | integer | Source-reported reaction count. |
| `emojiReactions.url` | string | URL to download the custom reaction image. |
| `emojiReactions.staticUrl` | string | URL to download the still reaction image. |
| `emojiReactions.accountIds` | array of strings | Available reacting-account IDs. This list may be incomplete. |
| `card` | object | Linked-page summary supplied with the post. The page link is in cardUrl. |
| `card.title` | string | Linked page title. |
| `card.description` | string | Linked page summary. |
| `card.authorName` | string | Credited author name. |
| `card.authorUrl` | string | Credited author link. |
| `card.providerName` | string | Credited provider name. |
| `card.providerUrl` | string | Credited provider link. |
| `card.imageUrl` | string | URL to download the linked-page card image from its source. |
| `card.width` | integer | Width in pixels. |
| `card.height` | integer | Height in pixels. |
| `card.publishedAt` | string | Source-reported page publication date. |
| `collections` | array of objects | Public collections tagged in this post. |
| `collections.id` | string | Source collection ID. |
| `collections.url` | string | Public collection link. |
| `collections.name` | string | Collection name. |
| `collections.ownerId` | string | Owner account ID on the instance that returned it. |
| `collections.createdAt` | string | Source creation time. |
| `collections.updatedAt` | string | Source update time. |
| `collections.itemsCount` | integer | Source-reported collection size. |
| `collections.memberIds` | array of strings | Available member identities. This list may be incomplete. |
| `quoteApproval` | object | Source quote-approval policy, not a current viewer permission. |
| `quoteApproval.automatic` | array of strings | Automatic approval policy entries. |
| `quoteApproval.manual` | array of strings | Manual approval policy entries. |
| `instance` | object | Public community information for the chosen instance. |
| `instance.title` | string | Public instance name. |
| `instance.shortDescription` | string | Short public description. |
| `instance.description` | string | Public description, which may contain HTML. |
| `instance.contactEmail` | string | Published contact email address. |
| `instance.languages` | array of strings | Published instance languages. |
| `instance.version` | string | Source-reported software version. |
| `instance.accountsCount` | integer | Source-reported aggregate account total. |
| `instance.postsCount` | integer | Source-reported aggregate post total. |
| `instance.registrationsOpen` | boolean | Whether the instance reports open registration. |
| `instance.approvalRequired` | boolean | Whether registration needs approval. |
| `instance.invitesEnabled` | boolean | Whether the instance reports enabled invites. |
| `instance.limits` | object | Published service limits, not Actor settings. |
| `instance.limits.maxPostCharacters` | integer | Maximum characters per post. |
| `instance.limits.maxMediaAttachments` | integer | Maximum media attachments per post. |
| `instance.limits.charactersReservedPerUrl` | integer | Characters reserved per URL. |
| `instance.limits.maxImageBytes` | integer | Maximum image size in bytes. |
| `instance.limits.maxVideoBytes` | integer | Maximum video size in bytes. |
| `instance.limits.maxImagePixels` | integer | Maximum image pixel count. |
| `instance.limits.maxVideoPixels` | integer | Maximum video pixel count. |
| `instance.limits.maxVideoFrameRate` | integer | Maximum video frames per second. |
| `instance.limits.maxPollOptions` | integer | Maximum poll options. |
| `instance.limits.maxPollOptionCharacters` | integer | Maximum characters per poll option. |
| `instance.limits.minPollExpiration` | integer | Minimum poll duration in seconds. |
| `instance.limits.maxPollExpiration` | integer | Maximum poll duration in seconds. |
| `instance.limits.supportedMimeTypes` | array of strings | Supported media MIME types. |
| `instance.rules` | array of objects | Published rules in source order. |
| `instance.rules.id` | string | Source rule ID. |
| `instance.rules.text` | string | Published rule text. |

**Example post row**

This complete row comes from the successful current-beta default-input run. Only whitespace has changed.

```json
{
  "resultType": "post",
  "id": "117516265520455560",
  "url": "https://masto.ai/@enog/117516265452916216",
  "text": "The lab at Drexel Discovery Day 2026 at the Convention Center 😄 Great job, everyone 🤩 #Laboratory #Science #Research #BiomedicalScience #Photography",
  "contentHtml": "<p>The lab at Drexel Discovery Day 2026 at the Convention Center 😄 Great job, everyone 🤩</p><p><a href=\"https://masto.ai/tags/Laboratory\" class=\"mention hashtag\" rel=\"nofollow noopener\" target=\"_blank\">#<span>Laboratory</span></a> <a href=\"https://masto.ai/tags/Science\" class=\"mention hashtag\" rel=\"nofollow noopener\" target=\"_blank\">#<span>Science</span></a> <a href=\"https://masto.ai/tags/Research\" class=\"mention hashtag\" rel=\"nofollow noopener\" target=\"_blank\">#<span>Research</span></a> <a href=\"https://masto.ai/tags/BiomedicalScience\" class=\"mention hashtag\" rel=\"nofollow noopener\" target=\"_blank\">#<span>BiomedicalScience</span></a> <a href=\"https://masto.ai/tags/Photography\" class=\"mention hashtag\" rel=\"nofollow noopener\" target=\"_blank\">#<span>Photography</span></a></p>",
  "author": {
    "id": "109377904124756028",
    "handle": "enog@masto.ai",
    "displayName": "Eishi Noguchi",
    "url": "https://masto.ai/@enog"
  },
  "favouritesCount": 0,
  "boostsCount": 0,
  "repliesCount": 0,
  "quotesCount": 0,
  "hashtags": [
    "laboratory",
    "science",
    "research",
    "biomedicalscience",
    "photography"
  ],
  "mentions": [],
  "links": [
    "https://masto.ai/tags/Laboratory",
    "https://masto.ai/tags/Science",
    "https://masto.ai/tags/Research",
    "https://masto.ai/tags/BiomedicalScience",
    "https://masto.ai/tags/Photography"
  ],
  "media": [
    {
      "type": "image",
      "url": "https://files.mastodon.social/cache/media_attachments/files/117/516/265/491/617/566/original/a346e7dc5adcf22a.jpeg",
      "previewUrl": "https://files.mastodon.social/cache/media_attachments/files/117/516/265/491/617/566/small/a346e7dc5adcf22a.jpeg",
      "id": "117516265491617566",
      "description": "Nine researchers and students stand together smiling for a group photo at a scientific conference. They are wearing conference name badges and posing in front of a research poster.",
      "original": {
        "width": 3325,
        "height": 2494
      },
      "preview": {
        "width": 554,
        "height": 416
      },
      "blurhash": "UHF~8DN{E4X6~ns:IWxZtlof-:%1i$Wn-mM}"
    }
  ],
  "publishedAt": "2026-10-28T02:51:45.000Z",
  "language": "en",
  "visibility": "public",
  "sensitive": false,
  "contentWarning": "",
  "emojis": [],
  "collections": [],
  "quoteApproval": {
    "automatic": [
      "public"
    ],
    "manual": []
  },
  "instance": {
    "title": "Mastodon.social",
    "shortDescription": "The original server of Mastodon, operated by Mastodon GmbH for the common good.\r\n\r\n",
    "description": "",
    "contactEmail": "staff@mastodon.social",
    "languages": [
      "en"
    ],
    "version": "5.0.0-nightly.2026-10-27",
    "accountsCount": 3452745,
    "postsCount": 193518352,
    "registrationsOpen": true,
    "approvalRequired": false,
    "invitesEnabled": true,
    "limits": {
      "maxPostCharacters": 500,
      "maxMediaAttachments": 4,
      "charactersReservedPerUrl": 23,
      "maxImageBytes": 16777216,
      "maxVideoBytes": 103809024,
      "maxImagePixels": 33177600,
      "maxVideoPixels": 8294400,
      "maxVideoFrameRate": 120,
      "maxPollOptions": 4,
      "maxPollOptionCharacters": 50,
      "minPollExpiration": 300,
      "maxPollExpiration": 2629746,
      "supportedMimeTypes": [
        "image/jpeg",
        "image/png",
        "image/gif",
        "image/webp",
        "video/webm",
        "video/mp4",
        "video/quicktime",
        "video/ogg",
        "audio/wave",
        "audio/wav",
        "audio/x-wav",
        "audio/x-pn-wave",
        "audio/vnd.wave",
        "audio/ogg",
        "audio/vorbis",
        "audio/mpeg",
        "audio/mp3",
        "audio/webm",
        "audio/flac",
        "audio/aac",
        "audio/m4a",
        "audio/x-m4a",
        "audio/mp4",
        "audio/3gpp",
        "video/x-ms-asf"
      ]
    },
    "rules": [
      {
        "id": "1",
        "text": "Sexually explicit or violent media must be marked as sensitive or with a content warning"
      },
      {
        "id": "2",
        "text": "No racism, sexism, homophobia, transphobia, ableism, xenophobia, or casteism."
      },
      {
        "id": "3",
        "text": "No incitement of violence or promotion of violent ideologies"
      },
      {
        "id": "4",
        "text": "No harassment, block evasion, dogpiling, or doxxing of others"
      },
      {
        "id": "7",
        "text": "Do not share information widely-known to be false and misleading"
      },
      {
        "id": "1008",
        "text": "Content created by others must be attributed, and use of generative AI must be disclosed"
      }
    ]
  }
}
```

**Profile row**

`resultType` is `profile`. The table includes all profile fields, including the shared instance details.

| Field | Type | What it does |
|---|---|---|
| `resultType` | string | Kind of result in this row. |
| `id` | string | Account ID on the Mastodon instance that returned it. |
| `handle` | string | Full account handle, with the user name and host. |
| `displayName` | string | Display name set by the account. |
| `url` | string | Public profile link. |
| `biography` | string | Public profile biography. |
| `avatarUrl` | string | Profile avatar image URL. |
| `headerUrl` | string | Profile header image URL. |
| `locked` | boolean | Whether the account requires approval for new followers. |
| `bot` | boolean | Whether the source labels this account as a bot. |
| `followersCount` | integer | Follower count reported by the instance. |
| `followingCount` | integer | Following count reported by the instance. |
| `postsCount` | integer | Post count reported by the instance. |
| `createdAt` | string | Account creation date reported by the source. |
| `emojis` | array of objects | Custom emojis supplied with this content. |
| `emojis.shortcode` | string | Emoji text code. |
| `emojis.url` | string | URL to download the emoji image, which may be animated. |
| `emojis.staticUrl` | string | URL to download the still emoji image. |
| `metadataFields` | array of objects | Public fields declared on the profile. |
| `metadataFields.name` | string | Field name. |
| `metadataFields.value` | string | Source-provided field value, which may contain HTML. |
| `metadataFields.verifiedAt` | string | Source-reported verification date. |
| `avatarDescription` | string | Avatar accessibility text. |
| `headerDescription` | string | Header image accessibility text. |
| `group` | boolean | Whether the source labels this as a group account. |
| `discoverable` | boolean | Whether the source marks this account as open to discovery. |
| `indexable` | boolean | Whether the source marks this account as open to indexing. |
| `lastPostDate` | string | Source-reported last-post date. |
| `instance` | object | Public community information for the chosen instance. |
| `instance.title` | string | Public instance name. |
| `instance.shortDescription` | string | Short public description. |
| `instance.description` | string | Public description, which may contain HTML. |
| `instance.contactEmail` | string | Published contact email address. |
| `instance.languages` | array of strings | Published instance languages. |
| `instance.version` | string | Source-reported software version. |
| `instance.accountsCount` | integer | Source-reported aggregate account total. |
| `instance.postsCount` | integer | Source-reported aggregate post total. |
| `instance.registrationsOpen` | boolean | Whether the instance reports open registration. |
| `instance.approvalRequired` | boolean | Whether registration needs approval. |
| `instance.invitesEnabled` | boolean | Whether the instance reports enabled invites. |
| `instance.limits` | object | Published service limits, not Actor settings. |
| `instance.limits.maxPostCharacters` | integer | Maximum characters per post. |
| `instance.limits.maxMediaAttachments` | integer | Maximum media attachments per post. |
| `instance.limits.charactersReservedPerUrl` | integer | Characters reserved per URL. |
| `instance.limits.maxImageBytes` | integer | Maximum image size in bytes. |
| `instance.limits.maxVideoBytes` | integer | Maximum video size in bytes. |
| `instance.limits.maxImagePixels` | integer | Maximum image pixel count. |
| `instance.limits.maxVideoPixels` | integer | Maximum video pixel count. |
| `instance.limits.maxVideoFrameRate` | integer | Maximum video frames per second. |
| `instance.limits.maxPollOptions` | integer | Maximum poll options. |
| `instance.limits.maxPollOptionCharacters` | integer | Maximum characters per poll option. |
| `instance.limits.minPollExpiration` | integer | Minimum poll duration in seconds. |
| `instance.limits.maxPollExpiration` | integer | Maximum poll duration in seconds. |
| `instance.limits.supportedMimeTypes` | array of strings | Supported media MIME types. |
| `instance.rules` | array of objects | Published rules in source order. |
| `instance.rules.id` | string | Source rule ID. |
| `instance.rules.text` | string | Published rule text. |

**Example profile row**

This complete row comes from a successful current-beta profile search. Only whitespace has changed.

```json
{
  "resultType": "profile",
  "id": "AgfvKUqz1e5AfCdbSy",
  "handle": "hellbeast@pleroma.envs.net",
  "displayName": "Regalia :neodog_science: ",
  "url": "https://pleroma.envs.net/users/hellbeast",
  "biography": "We power the overlap where the heart and technology merge I'm like if you took trauma and whitelabeled it as character Expect my thoughts about tech, Linux, Social VR and other shit transmitted to you over JSON-LD This account is chatty, often about weird and disconnected stuff, keep that in mind when if you want to follow me otherwise feel free to. Feel free to be affectionate / flirt (why would you) but don't expect anything, I'm fairly non-committal with that stuff. 18 but do not be lewd here, won't post NSFW things here. Come with a non-after-dark / SFW account if you can. I'm a dog, my ears are fluffy, my paws are soft, ruffle my hair :3 :therian: :transgender_flag: :pansexual_flag: :polyamory_flag: :neodog_floof:",
  "avatarUrl": "https://pleroma.envs.net/media/91dda770437bb6c52a9b53a8e862e0f204028716b7e49084a856a91169bc41c8.jpg",
  "headerUrl": "https://pleroma.envs.net/media/d8cb349c35c4624816c2f3a364d56512f2bfa5d3489b70a2d2bac6939e0c1b86.webp",
  "locked": false,
  "bot": false,
  "followersCount": 109,
  "followingCount": 125,
  "postsCount": 2654,
  "createdAt": "2024-04-08T10:16:34.000Z",
  "emojis": [
    {
      "shortcode": "neodog_floof",
      "url": "https://pleroma.envs.net/emoji/neodog/neodog_floof.png",
      "staticUrl": "https://pleroma.envs.net/emoji/neodog/neodog_floof.png"
    },
    {
      "shortcode": "neodog_science",
      "url": "https://pleroma.envs.net/emoji/neodog/neodog_science.png",
      "staticUrl": "https://pleroma.envs.net/emoji/neodog/neodog_science.png"
    },
    {
      "shortcode": "pansexual_flag",
      "url": "https://pleroma.envs.net/emoji/pride_flags/pansexual_flag.png",
      "staticUrl": "https://pleroma.envs.net/emoji/pride_flags/pansexual_flag.png"
    },
    {
      "shortcode": "polyamory_flag",
      "url": "https://pleroma.envs.net/emoji/pride_flags/polyamory_flag.png",
      "staticUrl": "https://pleroma.envs.net/emoji/pride_flags/polyamory_flag.png"
    },
    {
      "shortcode": "therian",
      "url": "https://pleroma.envs.net/emoji/custom/therian.png",
      "staticUrl": "https://pleroma.envs.net/emoji/custom/therian.png"
    },
    {
      "shortcode": "transgender_flag",
      "url": "https://pleroma.envs.net/emoji/pride_flags/transgender_flag.png",
      "staticUrl": "https://pleroma.envs.net/emoji/pride_flags/transgender_flag.png"
    }
  ],
  "metadataFields": [
    {
      "name": "Website",
      "value": "<a href=\"https://hellbeast.eu.org\" rel=\"ugc\">https://hellbeast.eu.org</a>",
      "verifiedAt": "2024-04-24T00:43:38.132185Z"
    },
    {
      "name": "NSFW Account",
      "value": "Ask if we know each other"
    },
    {
      "name": "pronouns.page",
      "value": "<a href=\"https://en.pronouns.page/@hellbeast\" rel=\"ugc\">https://en.pronouns.page/@hellbeast</a>",
      "verifiedAt": "2024-04-24T00:43:38.505756Z"
    },
    {
      "name": "Contacts",
      "value": "<a href=\"https://hellbeast.eu.org/contacts\" rel=\"ugc\">https://hellbeast.eu.org/contacts</a>"
    }
  ],
  "avatarDescription": "",
  "headerDescription": "",
  "lastPostDate": "2026-10-25",
  "instance": {
    "title": "Akkoma on envs.net",
    "shortDescription": "A Pleroma/Akkoma instance on envs.net, an alternative fediverse server.",
    "description": "A Pleroma/Akkoma instance on envs.net, an alternative fediverse server.",
    "contactEmail": "hostmaster@envs.net",
    "languages": [
      "en"
    ],
    "version": "2.7.2 (compatible; Akkoma 3.20.1-0-gbc62dd8--stable-)",
    "accountsCount": 366,
    "postsCount": 72560,
    "registrationsOpen": true,
    "approvalRequired": true,
    "limits": {}
  }
}
```

#### 💳 Pricing

The Actor charges for each post or profile saved to the dataset.

| Charged outcome | Free plan | Bronze, Silver, Gold, Platinum, or Diamond |
|---|---:|---:|
| One post | $0.0004 | $0.00035 |
| One profile | $0.0004 | $0.00035 |

Both prices are per saved row. Nested data has no separate Actor event. No post or profile event is charged for an empty search, skipped duplicate, or diagnostic message. The local pricing has no separate setup event. Check the Pricing tab for current prices. A run can stop at your charge limit before it reaches `maxItems`.

#### 🔌 Integrations

Download the dataset as JSON, CSV, or Excel from Apify Console. Read its rows through the Apify API, or use Apify schedules and webhooks as part of your workflow.

https://www.youtube.com/watch?v=bNACk1_S_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### Why does keyword search return fewer rows than I asked for?

The instance controls public search and its available pages. Some instances restrict post search or later search pages. The Actor can return the rows it read before that restriction. A limit is a cap, not a promise of that many matches.

##### Can I collect older account or hashtag posts?

Yes, where the instance provides more public feed pages. Leave `maxItems` empty or use 0 to collect all available rows. This does not promise a complete historical archive.

##### Can I collect an account on another instance?

Use its full `user@host` handle or exact public profile URL. It must be available through the chosen instance's public coverage. A bare username refers to the chosen instance.

##### What if two inputs find the same post?

The Actor saves the first eligible occurrence of that public URL once. It skips later occurrences and does not combine their boost context or other data.

##### Why are quoted-post details missing?

An instance may supply only a quote ID or some quote fields. The row keeps those available details and the parent post. The Actor does not fetch missing quote details separately.

##### Do media links download files into Apify storage?

No. The output contains file and preview URLs hosted by the public source. It does not copy the files into Apify storage.

##### Can this replace a full Mastodon API client?

It collects the public posts and profiles described here without a Mastodon login or source API key. It does not post content, access private messages, or provide complete fediverse coverage.

##### Can one run return posts and profiles?

No. Choose one result type per run. Post rows include author details, but a separate profile run returns profile rows.

### 📝 Changelog

**v0.0** (28-10-2026)

- Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~mastodon-scraper/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Bluesky Posts Search Scraper](https://apify.com/maximedupre/bluesky-search) searches public Bluesky posts and profiles for related topics.
- [Threads Search Post Scraper](https://apify.com/maximedupre/threads-search-post-scraper) collects public Threads posts by keyword, hashtag, or known post.
- [Substack Notes Scraper](https://apify.com/maximedupre/substack-notes) collects public Notes for topic and author research.
- [Truth Social Posts Scraper](https://apify.com/maximedupre/truth-social-posts) collects public posts from profile URLs.
- [Gab Posts Scraper](https://apify.com/maximedupre/gab-posts-scraper) collects public Gab posts and profiles.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `instanceUrl` (type: `string`):

Enter the full instance URL, such as https://mastodon.social. This instance is used for searches and feeds. Public post features also work on Pleroma, Akkoma, and other Mastodon-compatible servers where available.

## `maxItems` (type: `integer`):

Stop after this many output rows across all submitted values. Leave blank or use 0 for all available results. A positive number returns that many rows, or fewer if no more results are available.

## `resultType` (type: `string`):

Posts returns public posts. Profiles finds public accounts that match Keywords. Fields for the other result type are ignored.

## `postMethod` (type: `string`):

Choose one way to find posts. Fill in Accounts, Hashtags, or Keywords for that choice. Public timeline and Trending posts need no search values. Inactive search values are ignored. Keyword search, public timelines, and trending posts depend on what the instance makes public.

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

Enter one account per item. Use a bare username on the chosen instance, a full user@host handle, or a full profile URL such as https://mastodon.social/@Mastodon. Each item names one account whose public posts you want.

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

Enter one hashtag per item, such as science or #science. Returns posts in the chosen instance's available feed for each tag.

## `keywords` (type: `array`):

Used for Profiles and for Posts with Keyword search. Enter one search word or phrase per item. Each item is a separate search, so keep a whole phrase in one item. Required for these choices; ignored for other post choices.

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

For Posts with Account feeds or Hashtag feeds. Turn off to leave out replies. Ignored for other choices.

## `includeBoosts` (type: `boolean`):

For Posts with Account feeds or Hashtag feeds. Turn off to leave out boosts. Ignored for other choices.

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

For Posts with Account feeds or Public timeline. Turn on to return only posts with images or videos. Ignored for other choices.

## Actor input object example

```json
{
  "instanceUrl": "https://mastodon.social",
  "maxItems": 20,
  "resultType": "posts",
  "postMethod": "hashtags",
  "hashtags": [
    "science"
  ],
  "includeReplies": true,
  "includeBoosts": true,
  "onlyMedia": false
}
```

# Actor output Schema

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

Open the collected posts or profiles.

# 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 = {
    "instanceUrl": "https://mastodon.social",
    "maxItems": 20,
    "resultType": "posts",
    "hashtags": [
        "science"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/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 = {
    "instanceUrl": "https://mastodon.social",
    "maxItems": 20,
    "resultType": "posts",
    "hashtags": ["science"],
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/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 '{
  "instanceUrl": "https://mastodon.social",
  "maxItems": 20,
  "resultType": "posts",
  "hashtags": [
    "science"
  ]
}' |
apify call maximedupre/mastodon-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maximedupre/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/KcXjEhH1LR2uEp0uG/builds/3rEEhZdeovVeFXy7i/openapi.json
