Mastodon $1๐Ÿ’ฐ URL, Trend & Profile Scraper avatar

Mastodon $1๐Ÿ’ฐ URL, Trend & Profile Scraper

Pricing

from $1.00 / 1,000 results

Go to Apify Store
Mastodon $1๐Ÿ’ฐ URL, Trend & Profile Scraper

Mastodon $1๐Ÿ’ฐ URL, Trend & Profile Scraper

From $1/1K. Scrape trending Mastodon profiles and related posts from any Mastodon instance. Returns rich profile data, follower counts, bios, avatars, fields, and thread replies. Supports nested profile reviews or flat review output.

Pricing

from $1.00 / 1,000 results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Mastodon Scraper: Trending Profiles, Posts and Replies

Mastodon Scraper turns any public Mastodon server into structured data. Get profiles with bios, follower counts and custom fields, their recent posts and the replies underneath them, or bulk posts collected by hashtag at volume. Discover accounts through currently trending posts and the public profile directory, or paste exact profile and post links to scrape only what you want. Export to JSON, CSV or Excel, or pull results through the API. Defaults to mastodon.social and works on any public Mastodon instance.

Why This Scraper?

  • Three output shapes. Profiles with reviews nested inside, a flat stream of reviews, or bulk keyword posts, so you pick the shape your pipeline needs.
  • Two discovery sources, merged. Authors of currently trending posts and the active public directory, deduplicated automatically into one list.
  • Full conversation context. Every post's reply thread is captured alongside it, tagged separately from the profile's own posts.
  • Deep history on demand. Walk a single profile's post history, or a hashtag's public timeline, back through thousands of posts via automatic load-more pagination.
  • URL mode. Paste exact profile or post links to scrape only the accounts and posts you choose.
  • Works on any public Mastodon server. Point the instance setting at mastodon.social or any other server address.
  • Nothing is dropped from the author account. Every post and reply keeps its author's complete upstream account object; the post's own fields are the mapped set documented below (a poll, application info, and a few link-card details are not carried through).

Use Cases

  • Community and trend research: track who and what topics are trending on a Mastodon server over time.
  • Social listening: bulk-collect posts under a hashtag to study a topic or event at scale.
  • Account monitoring: watch specific profiles for new posts and replies on a recurring schedule.
  • Fediverse research and journalism: build datasets of public conversation threads for analysis.
  • Community growth tracking: monitor follower counts and posting activity for chosen accounts.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
recordType"review"
id"112233445566778899"
reviewType"post" (also "reply")
sourceKeyword"climate" (keyword mode only; null otherwise)
contentText"Sharing our monthly update on the project roadmap."
createdAt"2026-03-14T09:12:00.000Z"
language"en"
url"https://mastodon.example/@exampleuser/112233445566778899"
repliesCount14
reblogsCount37
favouritesCount120
inReplyToIdnull
isReblogfalse
rebloggedByAcctnull (set to the booster's handle when isReblog is true)
tags["climate", "sustainability"]
mediaAttachments[] (or the attached image/video objects)
sensitivefalse
authorAcct"exampleuser"
authorDisplayName"Example User"
authorUrl"https://mastodon.example/@exampleuser"
account{ ... complete upstream author object ... }

Profile records (profiles mode) carry acct, username, displayName, bio, followersCount, followingCount, statusesCount, url, avatar, createdAt and any custom profile fields the account has set, plus a nested reviews array (that profile's own posts and replies) and reviewCount. Review records also carry contentHtml, editedAt, visibility, spoilerText, inReplyToAccountId, mentions and link-card fields (cardUrl, cardTitle) alongside the columns above. Every profile and review record additionally keeps the complete upstream Mastodon account object under account, so nothing the source API returns is ever dropped.

How to Use

  1. Pick a mode: profiles (nested reviews under each profile), reviews (flat, one record per post or reply), or keyword (bulk posts by topic).
  2. For profiles or reviews mode, pick a profile source (trending, directory, or both), or paste profile and post URLs instead to scrape exactly those.
  3. Set the limits for your mode (max profiles, posts per profile, replies per post, or max posts for keyword mode) and turn Include replies on or off.
  4. Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.

Trending profiles with their reviews (default):

{
"mode": "profiles",
"profileSource": "both",
"includeReplies": true,
"maxProfiles": 10,
"maxReviewsPerProfile": 5,
"maxRepliesPerPost": 10
}

Reviews only, capped, no replies (faster and cheaper):

{
"mode": "reviews",
"profileSource": "trending",
"includeReplies": false,
"maxProfiles": 20,
"maxReviewsPerProfile": 10,
"maxReviews": 200
}

Bulk posts by keyword (high volume, load-more pagination):

{
"mode": "keyword",
"keywords": ["news", "art"],
"includeReplies": false,
"maxPosts": 5000
}

URL mode (specific profiles):

{
"mode": "reviews",
"urls": [
"https://mastodon.social/@Mastodon",
"https://mastodon.social/@Gargron"
],
"includeReplies": false,
"maxReviewsPerProfile": 3
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/mastodon-social-scraper").call(run_input={"mode": "keyword", "keywords": ["news"], "maxPosts": 100})
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
print(post["authorAcct"], post["contentText"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/mastodon-social-scraper').call({ mode: 'keyword', keywords: ['news'], maxPosts: 100 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Resume and recurring updates

Two different things, both optional:

  • Resume from a previous run (resumeFromRunId): paste a previous run or dataset ID to continue an interrupted crawl. The actor repeats the same search and skips every record it already collected there, so you are not billed twice for the same post or profile. It walks to the same depth as the original run rather than digging further into history, so resume soon after an interruption for the most complete pickup of anything still missing.
  • Incremental mode (incrementalMode): turn this on for recurring monitoring of the same search (daily or weekly). The actor remembers its own baseline in a key-value store, keyed by mode, instance and your query settings (the max* depth limits are excluded, so two runs of the same search with different limits still share one baseline; no run or dataset ID to paste). The first run returns everything as NEW. Later runs return only NEW, UPDATED and REAPPEARED records by default. Turn on emitUnchanged or emitExpired only if you also want those rows back, since both are extra billed items.

When incremental mode is on, every pushed record gains four fields: changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields (which fields differed from the last run), firstSeenAt and lastSeenAt.

EXPIRED rows (a profile or post that was present before but can no longer be found) are only produced once a run has scanned its whole tracked scope without being cut short by a cap, a failed page, an instance error, or Resume; a partial scan cannot tell "gone" apart from "not reached yet". Turning emitExpired on is also what starts tracking absence at all: with it off, a record that stops appearing simply keeps its last-known state instead of being marked gone, and a record can only be reported REAPPEARED after it was first marked gone by an earlier complete run that had emitExpired on. The actor does not distinguish a post the author deleted from one that disappeared for any other reason (moderation, an account closing, and so on); EXPIRED only means the record could no longer be found in a complete scan. In explore mode specifically, "complete scan" means every profile in that run's discovered top-N from the trending and directory listings was processed, not that every account on the instance was checked; since the trending list and the public directory's ranking can reshuffle between runs, an account that simply drops out of this run's top-N (while still existing) is indistinguishable from one that's genuinely gone, so emitExpired in explore mode can mark, and bill, EXPIRED rows for accounts that are still there. In keyword mode, a reply on a post whose parent turned out UNCHANGED this run is not re-fetched, so a run that completes normally can still tombstone replies it simply didn't re-check that run, which then bill again as REAPPEARED once the parent post next changes; this is a known limitation of keyword mode with Emit expired on, not a sign the reply is actually gone.

Use State key to name a monitoring campaign explicitly, or to intentionally share state across two differently-configured runs; leave it empty to let the actor derive one automatically so different searches never collide.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step that happens after the scrape; the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify โ†’ Settings โ†’ Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringprofilesprofiles (nested reviews), reviews (flat), or keyword (bulk posts by topic).
instanceUrlstringhttps://mastodon.socialAny public Mastodon server to read from.
profileSourcestringbothtrending, directory, or both, merged and deduped. Used only when no URLs are given.
onlyLocalbooleanfalseDirectory source: return only accounts local to the chosen instance.
urlsarray[]Profile or post URLs. When set, this overrides explore/profiles/reviews discovery. Keyword mode ignores urls entirely.
keywordsarray(none; prefill: ["news"])Topics to bulk-scrape in keyword mode, matched as tags (with or without a leading #).
includeRepliesbooleantrueFetch the reply thread under each post.
maxProfilesinteger10Minimum 1. Maximum profiles to scrape (profiles mode, or as review sources in reviews mode).
maxProfiles (max)integerMaximum 500.
maxReviewsPerProfileinteger5Minimum 1. Posts to fetch per profile, walked via load-more.
maxReviewsPerProfile (max)integerMaximum 5000.
maxRepliesPerPostinteger10Minimum 0. Replies to keep per post when Include replies is on.
maxRepliesPerPost (max)integerMaximum 200.
maxReviewsinteger0Minimum 0. Hard cap on total reviews returned in reviews mode (0 = unlimited, bounded by the profile and per-post limits).
maxPostsinteger500Minimum 1. Hard cap on total posts returned in keyword mode, split evenly across keywords.
maxPosts (max)integerMaximum 100000.
resumeFromRunIdstring(none)Continue one specific interrupted run or dataset, without returning or billing records already collected there.
incrementalModebooleanfalseTurn on for recurring monitoring; see "Resume and recurring updates" above.
stateKeystring(none)Name or share an incremental-mode monitoring campaign explicitly.
emitUnchangedbooleanfalseAlso return UNCHANGED records (incremental mode only; bills extra rows).
emitExpiredbooleanfalseAlso return EXPIRED records (incremental mode only; bills extra rows).
proxyobjectApify ProxyConnection settings.
mcpConnectorsarray(none)Optional: send a summary of each record to apps you authorized under Integrations.
notionParentPageUrlstring(none)Notion connector only: page under which item pages are created.
maxNotifyListingsinteger50Minimum 1. Cap on items written to each connector per run; does not affect the dataset.
maxNotifyListings (max)integerMaximum 1000.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"recordType": "profile",
"id": "990011223",
"acct": "exampleuser",
"username": "exampleuser",
"displayName": "Example User",
"bio": "Notes on open community projects and weekend hiking trips.",
"followersCount": 4820,
"followingCount": 312,
"statusesCount": 1560,
"url": "https://mastodon.example/@exampleuser",
"avatar": "https://files.mastodon.example/accounts/avatars/000/990/011/223/original/avatar.png",
"bot": false,
"createdAt": "2019-05-02T00:00:00.000Z",
"fields": [{ "name": "Homepage", "value": "example.org" }],
"reviewCount": 2,
"reviews": [
{
"recordType": "review",
"id": "112233445566778899",
"reviewType": "post",
"contentText": "Sharing our monthly update on the project roadmap.",
"createdAt": "2026-03-14T09:12:00.000Z",
"repliesCount": 14,
"reblogsCount": 37,
"favouritesCount": 120,
"url": "https://mastodon.example/@exampleuser/112233445566778899",
"authorAcct": "exampleuser"
},
{
"recordType": "review",
"id": "112233445566778900",
"reviewType": "reply",
"contentText": "Thanks for the detailed writeup, this is helpful context.",
"createdAt": "2026-03-14T09:40:00.000Z",
"inReplyToId": "112233445566778899",
"authorAcct": "sampleuser2"
}
]
}

Plan Requirement

The default connection works out of the box, and the actor moves to a fresh connection automatically when the current one is rate-limited. For large or frequent runs, a residential connection gives more headroom; pick it under Connection.

FAQ

How much does it cost?

You pay per record returned. In profiles mode, a surcharge applies once per profile whose replies were fetched and actually included in the pushed record; reviews and keyword mode never add this surcharge, since every reply there is already its own billed record. The Pricing tab shows the current rates. Use the max limits to cap the cost of any run.

This actor collects only information a Mastodon server already serves through its own public API to any visitor. You are responsible for how you use the result: usernames, bios and post content can identify real people, so follow the instance's terms, respect any account that has set itself to a locked or non-discoverable state, and get legal advice before any commercial redistribution.

Can I get only new or changed posts on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared records since the last run, and anything unchanged is not billed unless you turn Emit unchanged on.

What is the difference between Resume and Incremental mode?

Resume continues one specific interrupted run using a run or dataset ID you paste in; it is a one-time cleanup step. Incremental mode is for scheduling the same search again and again and getting a self-tracked diff each time, with no ID to paste. Use Resume after a run stops partway through; use Incremental mode for ongoing monitoring.

Why did my run finish with zero results instead of failing?

If a mode's source could not be reached at all, or nothing matched your input (an empty keyword list, URLs that do not resolve, and so on), the run finishes successfully with an explanatory status message and no dataset items, so you are never billed for a result you did not get. A run only fails outright when the input itself cannot be honored, such as a resumeFromRunId that does not resolve to a run or dataset you can access, or turning on Incremental mode together with resumeFromRunId while that search already has saved incremental state.

Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push a summary of each record into Notion, Linear or Airtable.

๐Ÿ”— Want more social media data?

Pair this actor with these related scrapers from the same team:

๐Ÿ“ฑ Truth Social Scraper
Scrape public Truth Social profiles, posts, media, metrics, author data, and complete...
๐Ÿ“ฑ TikTok Profile, Hashtag, Search, Video & Trending Scraper
Scrape TikTok without login. Extract profiles, bios, follower stats and videos; search by...
๐Ÿ“ฑ Lemon8 Search Scraper
Scrape Lemon8 posts by keyword across multiple regions. Extract posts, images, videos...
๐Ÿ“ฑ Lemon8 Profile Scraper
Scrape Lemon8 user profiles with automated multi-profile discovery. Extract profile data...
๐Ÿ‘— Lemon8 Feed Scraper
Scrape Lemon8 feeds across 22 categories and 10+ regions. Extract posts, images, videos...
๐Ÿ“‡ GoWork FR & DE Company Reviews and Profile Scraper
Extract company profiles from GoWork France and Germany, including contact details...

๐Ÿ‘‰ Browse all abotapi scrapers

๐Ÿ’ฌ Support & custom scrapers

  • ๐Ÿž Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • ๐Ÿ› ๏ธ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
  • โญ Enjoying it? A quick review on the actor page helps other users find it.