# Lemmy Scraper: Federated Communities & Posts (`arman-bd/lemmy-posts-scraper`) Actor

Scrape Lemmy instances: posts, communities, scores, comment counts and cross-instance federation data. An open alternative to Reddit's locked-down API.

- **URL**: https://apify.com/arman-bd/lemmy-posts-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Social media, News, Automation
- **Stats:** 2 total users, 1 monthly users, 91.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 post scrapeds

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

## Lemmy Scraper: Federated Communities & Posts

![Lemmy Scraper: Posts from any Lemmy instance, score, up/down vote split, comments and the federation trail across instances](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/lemmy-posts-scraper.jpg)

Reads posts from any Lemmy instance. You get the title, body, link, score, upvote and downvote split, comment count, community, author, and the federation trail showing which instance each post and author actually originated on.

Lemmy is the fediverse's Reddit-shaped link aggregator, and every instance exposes a fully open API v3 at `/api/v3`. That means no account, no OAuth app, no rate-limit quota, no proxy and no browser. For contrast, Reddit's own `.json` endpoints answer HTTP 403 to the same kind of anonymous request.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/lemmy-posts-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/lemmy-posts-scraper.md
```

### What you get

| Field | What it holds |
|---|---|
| `instance`, `postId`, `postUrl` | Instance you queried, its local post ID, and the canonical post link |
| `title`, `body`, `url` | Post title, self-text body, and the outbound link for link posts |
| `apId` | ActivityPub ID, which is the post's true origin URL across the fediverse |
| `communityName`, `communityTitle`, `communityUrl`, `communityInstance` | Community slug, display name, actor URL and the instance it lives on |
| `creatorName`, `creatorDisplayName`, `creatorUrl`, `creatorInstance`, `creatorIsBot` | Author handle, display name, actor URL, home instance and bot flag |
| `score`, `upvotes`, `downvotes`, `commentCount` | Full vote breakdown, since Lemmy publishes up and down separately, unlike Reddit |
| `published`, `updated`, `newestCommentAt` | Post timestamps and the most recent comment time |
| `featured`, `locked`, `nsfw` | Pinned in community or site-wide, locked to new comments, NSFW flag |
| `thumbnailUrl`, `embedTitle`, `embedDescription` | Link preview metadata |
| `comments` | Optional array of top-scoring top-level comments with their own scores |
| `scrapedAt` | When the run happened |

`RUN_SUMMARY` in the key-value store holds per-run counts, the filters you used, and any target that failed.

### Use cases

- **Tech discussion tracking.** Read `!technology@lemmy.world` and `!programming@programming.dev` in one run, outside Reddit entirely.
- **Brand and product monitoring.** Sweep several instances on a schedule and search the bodies you collect.
- **Newsletter curation.** `TopWeek` on a handful of communities gives you a ranked shortlist every Monday.
- **Federation research.** `communityInstance` versus `creatorInstance` shows how content actually crosses instances.
- **Sentiment work.** The separate `upvotes` and `downvotes` split supports controversy scoring that a net score cannot.

### Quick start

The front page of one instance:

```json
{
 "instances": ["lemmy.world"],
 "maxPostsPerInstance": 50
}
```

One community across three instances, ranked by the week:

```json
{
 "instances": ["lemmy.world", "programming.dev", "https://lemmy.ml"],
 "communities": ["technology"],
 "sortBy": "TopWeek",
 "maxPostsPerInstance": 100
}
```

Discussion-heavy posts with their top replies:

```json
{
 "instances": ["lemmy.world"],
 "communities": ["technology", "asklemmy"],
 "sortBy": "MostComments",
 "maxPostsPerInstance": 40,
 "includeComments": true,
 "maxCommentsPerPost": 10
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `instances` | array | `["lemmy.world"]` | Hostnames or URLs. Each is queried independently. |
| `communities` | array | `[]` | Applied to every instance. Accepts `technology`, `!technology@lemmy.world` or a `/c/` URL. Empty means each instance's whole front page. |
| `sortBy` | string | `Hot` | `Hot`, `Active`, `New`, `Scaled`, `TopDay` through `TopAll`, `MostComments`, `NewComments`, `Controversial`. |
| `maxPostsPerInstance` | integer | `50` | Total per instance. With several communities the budget is split evenly. |
| `includeComments` | boolean | `false` | Fetch top-level comments per post. Costs one extra request per post that has any. |
| `maxCommentsPerPost` | integer | `5` | Comments kept per post, highest score first. Ignored when `includeComments` is off. |

A community that does not exist on an instance returns HTTP 404 and is recorded as a failure, and the other instances carry on. Because Lemmy federates, a community named on one instance is usually readable from several: `!technology@lemmy.world` seen through `lemmy.ml` returns that instance's federated copy.

### Output example

```json
{
 "instance": "lemmy.world",
 "postId": 50183504,
 "title": "New EU law requires devices to be repairable",
 "body": "The products covered by the obligation to repair currently include:\n\n- washing machines .",
 "url": "https://www.nporadio1.nl/fragmenten/nos-met-het-oog-op-morgen/.",
 "postUrl": "https://lemmy.world/post/50183504",
 "apId": "https://piefed.zip/c/technology@lemmy.world/p/1699468/new-eu-law-requires-devices-to-be-repairable",
 "communityName": "technology",
 "communityTitle": "Technology",
 "communityUrl": "https://lemmy.world/c/technology",
 "communityInstance": "lemmy.world",
 "creatorName": "inari",
 "creatorDisplayName": "inari",
 "creatorUrl": "https://piefed.zip/u/inari",
 "creatorInstance": "piefed.zip",
 "creatorIsBot": false,
 "score": 1764,
 "upvotes": 1768,
 "downvotes": 4,
 "commentCount": 283,
 "published": "2026-08-01T20:40:59.572119Z",
 "updated": "2026-08-01T20:42:20.713133Z",
 "newestCommentAt": "2026-08-06T10:29:32.713285Z",
 "featured": false,
 "locked": false,
 "nsfw": false,
 "thumbnailUrl": "https://radioimages.npox.nl/RAD1/image/.webp",
 "embedTitle": "Apparaten verplicht repareerbaar door nieuwe EU-wet",
 "embedDescription": "Iedere wasmachine, telefoon of televisie die je vanaf morgen in Europa koopt .",
 "comments": [
 {
 "commentId": 25077535,
 "body": "Hopefully this will benefit other countries indirectly .",
 "creatorName": "dan1101",
 "score": 199,
 "upvotes": 199,
 "downvotes": 0,
 "published": "2026-08-01T21:01:30.164757Z",
 "url": "https://lemmy.world/comment/25077535"
 }
 ],
 "scrapedAt": "2026-08-06T11:42:30.835Z"
}
```

### Finding a community name

Lemmy writes community addresses three ways, and all three work here.

| What you see | What to paste |
|---|---|
| `!technology@lemmy.world` | `technology`, or the whole handle. The `!` and the domain are stripped |
| `https://lemmy.world/c/technology` | The whole URL |
| The sidebar heading "Technology" | `technology`, the slug rather than the display title |

Put the domain in `instances` and the slug in `communities`. To read the same community from two instances, list both instances and one community name.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~lemmy-posts-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "instances": ["lemmy.world"],
 "communities": ["technology"],
 "sortBy": "TopWeek",
 "maxPostsPerInstance": 50
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/lemmy-posts-scraper').call({
 instances: ['lemmy.world', 'programming.dev'],
 communities: ['technology'],
 sortBy: 'TopWeek',
 maxPostsPerInstance: 25,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const post of items) {
 console.log(`[${post.score}] ${post.title} - ${post.communityName}@${post.instance}`);
}
```

### Notes

- Lemmy caps a page at 50 posts. Asking for more returns `couldnt_get_posts`, so the Actor pages 50 at a time using 1-based `page` numbers until your budget is met.
- `limit` does not work on the comments endpoint. When `max_depth` is set Lemmy returns the whole depth-1 tree regardless of `limit`, so `maxCommentsPerPost` is applied after the response arrives.
- Federation means the origin instance differs from the one you queried. `communityInstance` and `creatorInstance` are parsed out of the ActivityPub actor URLs, so a post read from `lemmy.world` may well have been written on `piefed.zip`.
- Every instance runs its own Lemmy version. The Actor targets API v3, which every current instance serves. `/api/v4` was not yet live on any instance tested.
- One bad target won't kill the run. A missing community returns 404 and lands in `RUN_SUMMARY.failures`, and the Actor only errors out if every target fails.

### FAQ

**Do I need a Lemmy account?** No. You supply no credentials.

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**How is this different from scraping Reddit?** Reddit gates its API behind registered apps, quotas and pricing. Lemmy publishes the same shape of data, meaning link posts, scores and threaded comments, on an open endpoint, so the run is deterministic and free of blocks.

**Can I get every post in a community?** Raise `maxPostsPerInstance` and use `sortBy: "New"`. The Actor keeps paging until the feed is exhausted or the cap is hit.

**Why do some posts have an empty `body`?** They are link posts. `body` is the self-text field and is only populated on text posts, so `url`, `embedTitle` and `embedDescription` carry the link instead.

**Can I plug it into something else?** Yes. Apify API, the client libraries, webhooks, scheduled runs, dataset exports to JSON, CSV or Excel, or MCP. The output is structured JSON.

# Actor input Schema

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

Instance hostnames or URLs, such as lemmy.world, https://lemmy.ml or programming.dev. Each is queried independently. Defaults to lemmy.world if left empty.

## `communities` (type: `array`):

Community names to read, applied to every instance. Accepts 'technology', '!technology@lemmy.world' or a /c/ URL. Leave empty to read each instance's whole front page, including federated posts.

## `sortBy` (type: `string`):

Feed ordering. Hot balances score against age, Top windows rank purely by score inside that period, and New is strictly chronological.

## `maxPostsPerInstance` (type: `integer`):

Total posts to save per instance. When several communities are listed the budget is split evenly between them. Posts arrive 50 per request.

## `includeComments` (type: `boolean`):

Fetch the highest-scoring top-level comments for each post. This costs one extra request per post with comments, so a 500-post run becomes roughly 510 requests.

## `maxCommentsPerPost` (type: `integer`):

How many top-level comments to keep per post, highest score first. Ignored unless 'Include top comments' is on.

## Actor input object example

```json
{
  "instances": [
    "lemmy.world",
    "programming.dev",
    "https://lemmy.ml"
  ],
  "communities": [
    "technology",
    "programming"
  ],
  "sortBy": "Hot",
  "maxPostsPerInstance": 50,
  "includeComments": false,
  "maxCommentsPerPost": 5
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN_SUMMARY record from the run's key-value store.

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "instances": [
        "lemmy.world"
    ]
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = { "instances": ["lemmy.world"] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/lemmy-posts-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 '{
  "instances": [
    "lemmy.world"
  ]
}' |
apify call arman-bd/lemmy-posts-scraper --silent --output-dataset

```

## MCP server setup

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