# Lemmy Scraper (`dami_studio/lemmy-scraper`) Actor

Lemmy scraper for fediverse posts from any instance: the front page, one community or a keyword search. Each row has the title, markdown body, link, author, community, score, upvotes, downvotes, comment count and the thread link. No login. $1.50 per 1,000 posts.

- **URL**: https://apify.com/dami_studio/lemmy-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, News, Integrations
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.44 / 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

**Scrape Lemmy posts from any instance, by feed, community or keyword**: give it a Lemmy host and a
mode, and get one row per post with the title, the markdown body, the external link, author,
community, score, upvotes, downvotes, comment count and the thread permalink.

The host is the first choice you make here, not an afterthought. Every Lemmy server holds only what
it has federated in, so the same community asked on two hosts comes back different, and a small
server has seen less of the network than a large one.

| | |
|---|---|
| **Input** | One instance host, plus a community name or a search term |
| **Output** | One row per post: title, markdown body, link, author, community, score, votes, comment count, permalink |
| **Ceiling** | 1,000 posts per run |
| **Account needed** | None, and no API key |
| **Price** | $1.50 per 1,000 posts on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 3,300 |

### 🔍 What Lemmy Scraper does

Three modes off the same host. `feed` reads the instance front page. `community` reads one
community, either bare like `technology` or fully qualified like `technology@lemmy.world` when the
community lives on another server. `search` runs the instance's own keyword search.

It pages 50 posts at a time until it has the number you asked for or the instance runs out,
deduplicates on post id inside the run, and stops early when a page comes back short.

One host per run. If you want three instances, that is three runs.

### 📋 What data you get from each Lemmy post

| What you get | Field |
|---|---|
| The post's id, title and thread link | `id`, `title`, `postUrl` |
| The page it points to, and its thumbnail | `url`, `thumbnail` |
| The post text, as markdown | `body` |
| Who posted it, with their federated address | `author`, `authorActor` |
| The community, by name and by title | `community`, `communityTitle` |
| Score, upvotes, downvotes and comment count | `score`, `upvotes`, `downvotes`, `comments` |
| When it was posted, and the NSFW flag | `published`, `nsfw` |

### ▶️ How to scrape Lemmy posts

1. Open [Lemmy Scraper](https://apify.com/dami_studio/lemmy-scraper) and click **Try for free**.
2. Leave **Lemmy instance** at `lemmy.world`, or type another host.
3. Pick a **Mode**, and fill in **Query** unless you chose the front-page feed.
4. Set **Max posts**. Start around 20 to see the shape of the output, then click **Start**.
5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.

### 💰 How much does it cost to scrape Lemmy posts?

**$1.50 per 1,000 posts** on the Free plan. Paid plans pay a little less: $1.48 on Starter, $1.46 on Scale and $1.44 on Business. On the free plan, the $5 Apify
gives you each month covers about 3,300 posts.

You pay for posts delivered. Duplicates dropped inside the run and diagnostic rows are not charged,
and a search that finds nothing costs you nothing.

### 📥 What you give it

```json
{
  "instance": "lemmy.world",
  "mode": "community",
  "query": "technology",
  "sort": "TopWeek",
  "maxItems": 200
}
```

| Field | Default | What it is |
|---|---|---|
| `instance` | `lemmy.world` | Bare host, no `https://`. `lemmy.ml`, `beehaw.org` and `sh.itjust.works` work the same way. |
| `mode` | `feed` | `feed`, `community` or `search`. |
| `query` | none | The community name in `community` mode, the keywords in `search` mode, ignored in `feed`. The Console box starts at `technology`; an API call has to send its own. |
| `sort` | `Hot` | `Hot`, `Active`, `New`, `TopDay`, `TopWeek`, `TopMonth` or `TopAll`. A value it does not recognise falls back to `Hot` and says so in the log. |
| `maxItems` | `100` | 1 to 1,000. |
| `notionConnector` | none | Optional. Writes one Notion page per post when the run finishes. Authorise the connector once under Settings, API & Integrations, MCP connectors. |
| `notionParentId` | none | Optional. The Notion data source to write into. Leave it empty and the pages land privately in your workspace. |
| `proxyConfiguration` | off | Optional network settings. A normal run does not need them. |

`query` is required in `community` and `search` mode. Leave it out and the run returns a `BAD_INPUT`
row instead of posts.

### 📤 What you get back

A real row from a run on 27 September 2026, with the body cut short and the image links trimmed:

```json
{
  "ok": true,
  "id": 52426536,
  "title": "How is everyone dealing with Android 17's \"Nearby Devices\" change?",
  "url": "https://lemmy.world/pictrs/image/526f113d-6ee0-...jpeg",
  "body": "It took me a while to figure out why many of my self-hosted services were intermittently failing to connect on my phone after updating to Android 17. I eventually figured out there's a new [ACCESS_LOCAL_NETWORK](https://developer.android.com/reference/android/Manifest.permission#ACCESS_LOCAL_NETWORK) permission ...",
  "author": "pHr34kY",
  "authorActor": "https://lemmy.world/u/pHr34kY",
  "community": "selfhosted",
  "communityTitle": "Selfhosted",
  "score": 11,
  "comments": 1,
  "upvotes": 11,
  "downvotes": 0,
  "nsfw": false,
  "thumbnail": "https://lemmy.world/pictrs/image/1de761ec-8cd1-...jpeg",
  "published": "2026-09-27T12:46:21.130523Z",
  "postUrl": "https://lemmy.world/post/52426536"
}
```

| Field | How to read it |
|---|---|
| `url` and `postUrl` | Two different links, and the difference matters. `url` is the external page or image the post points at, and is `null` on a text-only post. `postUrl` is the Lemmy thread itself and is always there. |
| `body` | The post text as markdown, so `**bold**` stays `**bold**` and links stay links. Stray HTML is cleaned out. `null` or empty when the post is just a link or an image. |
| `authorActor` | The federated user URL. That is the identity that stays stable across instances, not `author`. |
| `comments` | The count. The comment text itself is not part of this actor. |
| `downvotes` | `0` on instances that switch downvotes off, which several do as a moderation choice. That is a server setting, not unanimous approval. |
| `id` | Stable. Use it as your deduplication key across runs. |

### 🧾 Reading the output

Two kinds of row land in your dataset.

| Row | How to spot it | Charged |
|---|---|---|
| A post | `ok: true` and a `postUrl` | yes |
| A diagnostic | `ok: false` and an `errorCode` | no |

Filter on `ok == true` and you have your posts.

| Code | What it means |
|---|---|
| `BAD_INPUT` | The host is not a valid hostname, the mode is unknown, or `query` is missing in `community` or `search` mode. |
| `NOT_FOUND` | The host is not a Lemmy instance, or that community is not on it. Check the spelling, or use the fully qualified `name@host` form. |
| `NO_RESULTS` | Host and community were both fine, there was simply nothing to return. |
| `NOT_SERVED` | The instance did not serve this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here, and runs from 13 to 24 October wrote `NETWORK`. |
| `RATE_LIMITED` | The instance asked for a slower pace. Try again in a few minutes, or ask for fewer posts. |
| `SERVER_ERROR` | The instance answered with a server error. It usually passes on its own. |
| `NETWORK` | The instance could not be reached. |
| `TIME_LIMIT` | The run stopped just before its time limit. The posts written before it are yours and charged; give the run more time to get the rest. |

A diagnostic row never fails the run, `TIME_LIMIT` included: a run the time limit stopped before any post came back still ends as succeeded, with an amber status message.

### 💡 What people use it for

- Watching one community on a schedule with `sort` set to `New` and a small `maxItems`, and diffing
  each run against the one before it on `id`.
- Reading what actually got traction in a niche community last month with `TopMonth`, where `score`
  next to `comments` separates what people upvoted from what they argued about.
- Keyword monitoring, with realistic expectations. Lemmy is small, so you get genuine mentions and
  not many of them.
- Filing a community's posts into Notion as pages, straight off the run.

### 🚧 What it does not do

- **No comment text.** You get `comments` as a count and nothing else.
- **One host per run.** Comparing instances means one run each.
- **You see only what the host has federated in.** A server that has defederated from another will
  not show you its posts, and a small server has simply seen fewer of them.
- **Search ranking belongs to the instance**, and this actor does not re-order it.
- **1,000 posts per run is the hard ceiling**, whatever the community holds.
- **If a page fails partway through a long run, the run ends with a diagnostic row** rather than the
  posts it had already collected. Re-run it, ideally with a smaller `maxItems`.
- **Public posts only.** No logins, nothing private, nothing already removed.
- It is not an official Lemmy tool. Dami's Studio is independent and is not affiliated with or endorsed by Lemmy, or by any other company named on this page.

### 🧭 Which social scraper do you need?

| If you want | Use |
|---|---|
| Lemmy posts from any instance, by feed, community or keyword | This one |
| Mastodon posts by hashtag, account or timeline | [Mastodon Scraper](https://apify.com/dami_studio/mastodon-scraper) |
| Bluesky posts and profiles | [Bluesky Scraper](https://apify.com/dami_studio/bluesky-scraper) |
| Hacker News stories and comments | [Hacker News Scraper](https://apify.com/dami_studio/hacker-news-scraper) |

### ❓ Questions people ask

#### Do I need a Lemmy account to scrape posts?

No. Everything it reads is public, and there is nothing to sign up for.

#### Which Lemmy instance should I pick?

The one whose community you actually want. For a community hosted elsewhere, use the qualified
`name@host` form so the server you are asking knows where to look.

#### Why did the same search give different answers on two hosts?

Because each holds a different slice of the network. That is federation working normally, not a
fault in the run.

#### Can I get the comments too?

Not from this one. Only the count comes back.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/lemmy-scraper/api/python) has code for Python, JavaScript and the CLI. MCP
clients like Claude and ChatGPT use `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/lemmy-scraper`;
coding assistants can load [our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-social-communities-SKILL.md). All
three bill your Apify account at the same price.

#### Is it legal to scrape Lemmy posts?

These are public posts on public instances. They still contain personal data, which GDPR and similar
laws cover, so have a reason for collecting it. Apify's write-up on
[the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good place to
start, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the instance, the mode and the run ID. The
`errorCode` on the diagnostic row usually names the problem on its own.

# Actor input Schema

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

The Lemmy instance host to scrape (bare domain, no https://). Examples: lemmy.world, lemmy.ml, beehaw.org, sh.itjust.works.

## `mode` (type: `string`):

What to scrape: "feed" = the instance front-page feed; "community" = a single community (put its name in Query); "search" = search posts by keyword (put the term in Query).

## `query` (type: `string`):

For mode "community": the community name, e.g. "technology" or "technology@lemmy.world". For mode "search": the keywords to search for, e.g. "linux". Ignored in mode "feed".

## `sort` (type: `string`):

How to sort posts. Hot/Active rank by recent engagement; New is chronological; the Top\* options rank by score within a time window.

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

Maximum number of posts to return. The actor paginates (50 per request) until it reaches this many or runs out of posts.

## `notionConnector` (type: `string`):

Optional. Write each post as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default), results are always saved to the dataset regardless.

## `notionParentId` (type: `string`):

Optional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.

## `proxyConfiguration` (type: `object`):

Optional, and not needed for a normal run.

## Actor input object example

```json
{
  "instance": "lemmy.world",
  "mode": "feed",
  "query": "technology",
  "sort": "Hot",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Scraped rows are stored in the default dataset (one row per result). Blocked/empty/error runs return a single uncharged diagnostic row instead.

# 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": "lemmy.world",
    "query": "technology"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/lemmy-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": "lemmy.world",
    "query": "technology",
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/lemmy-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": "lemmy.world",
  "query": "technology"
}' |
apify call dami_studio/lemmy-scraper --silent --output-dataset

```

## MCP server setup

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