# Lemmy Posts, Comments, Threads & Communities (no login) (`yadroo/lemmy-public-posts`) Actor

Public Lemmy data as rows: posts of named communities or of the whole site, keyword search over posts and comments, the full comment tree of any post, a user's public posts and comments, and community size and activity counts. Author and community server on every row. Keyless API, UTC dates.

- **URL**: https://apify.com/yadroo/lemmy-public-posts.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Social media, News, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.05 / 1,000 lemmy data rows

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

Public **Lemmy** content as flat rows, read from the HTTP API of **lemmy.world** — the largest instance of the network — which answers JSON without an account, a token or a cookie. Six questions in one actor: the posts of the communities you name, the site-wide feed, a keyword across posts **and** comments, the whole discussion under a post numbered by reply depth, what an account published in public, and which communities exist for a topic with their subscriber and activity counts. Lemmy is federated, so that one server also holds content from thousands of other instances: **every row names the server its author and its community live on**, and every timestamp is UTC ISO-8601.

> **Need it tailored to your workflow?** Missing a field, a filter, another source or a different output format? [Open an issue](https://apify.com/yadroo/lemmy-public-posts/issues) and describe what you need — we read every request and adapt the actor.

### Use cases

- **Keep a topic feed after a forum closes its own.** Point `communityPosts` at `technology`, `asklemmy` or any community, set `sinceHours` to your schedule interval and `onlyNew: true` — each run then holds exactly what appeared since the last one.
- **Watch a product name where opinions actually are.** `searchPosts` finds the posts, `searchComments` the replies; on Lemmy most of what people think about a release sits in comments, and both modes reach communities hosted on other instances.
- **Summarise a discussion.** `postComments` turns one post link into its thread in reading order, with `replyDepth`, `parentCommentId` and `childCount`, which is the shape an LLM needs to answer "what did people reply, and to whom".
- **Archive public announcements.** `userActivity` exports what a moderator, admin or project account posted and commented, with the lifetime post and comment totals the profile shows.
- **Size the network before you monitor it.** `communities` ranks communities for a topic by subscribers and users active per day, week and month, so a listening pipeline starts with the communities that are alive.
- **One feed for a dashboard.** `communityPosts` with no community named is the site-wide feed; `listingType: local` keeps it to the communities lemmy.world hosts itself, `all` opens it to the federated view.
- **Study federation.** `authorInstances` and `communityInstances` filter on the server columns — watch one instance's voices inside a worldwide community, or drop an instance that floods a feed.

### Input

Every field is optional and each mode uses the fields of its own section. Values shown in the editor are **prefills** — change or clear them freely; only behaviour settings have real defaults.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string | `communityPosts` | `communityPosts`, `searchPosts`, `searchComments`, `postComments`, `userActivity`, `communities` — see [Modes](#modes) |
| `communities` | string\[] | prefill `technology`, `asklemmy` | Communities to read. `name`, `name@server` or a community link — see [Writing a community or a handle](#writing-a-community-or-a-handle). Empty in `communityPosts` = the site-wide feed |
| `listingType` | string | `all` | `all` = every federated server this one knows, `local` = communities hosted on lemmy.world. Applies to the site-wide feed, both search modes and the community directory |
| `sort` | string | `New` | 16 orders for posts, comments and communities — see [Sort orders](#sort-orders) |
| `searchQuery` | string | prefill `kubernetes` | The keyword for `searchPosts`, `searchComments` and the community directory. One phrase per run, matched as a case-insensitive substring |
| `postUrls` | string\[] | prefill one post link | `postComments`: a lemmy.world link, a bare post id, or a post link on any other instance — see [Post links](#post-links) |
| `includeOriginalPost` | boolean | `true` | `postComments`: write one `post` row per thread before its comments |
| `maxCommentDepth` | number | `8` | 1–50. Levels of replies to read in `postComments`; 1 = direct replies only |
| `commentSort` | string | `Top` | `Top`, `New`, `Old`, `Hot`, `Controversial` — the order of the branches in `postComments` |
| `users` | string\[] | prefill `ruud@lemmy.world` | `userActivity`: accounts as `name`, `name@server` or a profile link. The row limit is split over them |
| `userContent` | string | `posts` | `userActivity`: `posts`, `comments` or `both` (split evenly, newest first) |
| `languages` | string\[] | all | Keep rows whose declared language is one of 22 codes, `und` included — see [Languages](#languages) |
| `authorInstances` | string\[] | all | Keep rows whose author lives on one of these servers, e.g. `lemm.ee`, `programming.dev` |
| `communityInstances` | string\[] | all | Keep rows whose community is hosted on one of these servers |
| `minScore` | number | – | −100 000…100 000. Keep posts and comments with at least this score (upvotes minus downvotes) |
| `minComments` | number | – | 0–100 000. Keep posts with at least this many comments. Comment rows carry `childCount` instead |
| `excludeNsfw` | boolean | `true` | Drop posts, comments and communities flagged NSFW. Switch off for a complete sample |
| `excludeBots` | boolean | `false` | Drop rows of accounts that declare themselves bots (`authorIsBot`) |
| `sinceHours` | number | – | 1–8760. Rolling window in UTC hours. With `sort: New` paging also stops as soon as the source walks past it |
| `onlyNew` | boolean | `false` | Remember the post and comment ids written — as they are written — and write only unseen ones on the next run of the same actor or task |
| `maxItems` | number | `50` | 1–1000. Hard cap on rows and therefore on cost; split over the communities, users or threads of the run. The run also stops at your maximum cost per run |
| `fields` | string\[] | all | Keep only these columns, in this order; `rowType`, `sourceMode`, `found` and `url` are always kept. Letter case is corrected, unknown names are reported in the status message with the nearest column, and a list with no valid column fails the run |

### Reference

#### Modes

| `mode` | Endpoints used | Rows | Dataset view |
|---|---|---|---|
| `communityPosts` | `post/list` | one `post` per post of the named communities, or of the whole instance | Posts |
| `searchPosts` | `search` (`Posts`) | one `post` per match in a title, body or posted link | Posts |
| `searchComments` | `search` (`Comments`) | one `comment` per match in comment text | Comments |
| `postComments` | `resolve_object` → `post` → `comment/list` | one `post` row (optional) plus one `comment` per reply, in reading order | Comments |
| `userActivity` | `user` | one `post` or `comment` per public item of the named accounts | Posts / Comments |
| `communities` | `community`, `search` (`Communities`), `community/list` | one `community` per community, with counts | Communities |

Each mode fills one view; the others stay empty. `communities` answers the communities you name (with `moderatorCount`), otherwise the matches for `searchQuery`, otherwise the ranked directory for `sort` and `listingType`.

#### Sort orders

`New`, `Old`, `Hot`, `Active`, `Scaled`, `MostComments`, `NewComments`, `Controversial`, `TopHour`, `TopSixHour`, `TopTwelveHour`, `TopDay`, `TopWeek`, `TopMonth`, `TopYear`, `TopAll`.

A `Top*` order is a **window** as well as an order: `TopWeek` can never return a post older than seven days, so do not combine it with a longer `sinceHours` and expect more rows. `New` is the one order that walks strictly down the clock, which makes it the cheapest choice for a monitoring run — it is the only one where a window can end the paging early. `Active` and `NewComments` rank by recent comment activity, `Scaled` lifts smaller communities, `Controversial` surfaces what was up- and downvoted alike. Comment branches use the shorter list: `Top`, `New`, `Old`, `Hot`, `Controversial`.

#### Writing a community or a handle

A bare `technology` means the community of that name on lemmy.world. `name@server` (`programming@programming.dev`, `climate@slrpnk.net`) means the federated copy this server keeps of a community hosted elsewhere. Links work too: `https://lemmy.world/c/technology`, `https://programming.dev/c/programming`, and the `!name@server` form people paste from chat. Accounts follow the same rules with `/u/`: `ruud`, `ruud@lemmy.world`, `https://lemm.ee/u/sunaurus`. A name this server has never seen yields one row with `found: false` and the reason in `notFoundReason`, so one typo in a long list never disappears silently. The `authorHandle` column is always written as `name@server`, so rows from different instances can be compared.

#### Post links

`postComments` accepts `https://lemmy.world/post/920294`, the bare id `920294`, and a post link on any other instance such as `https://programming.dev/post/57357662`. Every instance numbers posts itself, so a foreign link is first resolved to the copy lemmy.world holds (one extra request), and the row then carries the local `postId` and `url` plus the canonical `apId` of the origin server. Any post row of any mode already carries `postId` and `url`, which is how a feed or search run chains into a thread run.

#### Languages

`und` (not set), `en`, `de`, `fr`, `es`, `pt`, `it`, `nl`, `sv`, `da`, `nb`, `fi`, `pl`, `cs`, `ru`, `uk`, `tr`, `ja`, `zh`, `ko`, `ar`, `ca`. Only these codes are accepted: Apify refuses any other value - a name like `English`, a three-letter code like `eng` - before the run starts, so a run never keeps a language you did not pick. Read the honest part first: Lemmy stores a language per post and comment, but most clients never set it, so a large share of real rows carry `und`. Pick `en` **and** `und` together if you want English-speaking communities without losing most of their content.

#### Row types

`rowType` is `post`, `comment` or `community`; `sourceMode` repeats the mode that produced the row, and `found` is `false` only on a row that explains an input the source would not serve.

### Examples

Posts of one community, last two days, newest first:

```json
{ "mode": "communityPosts", "communities": ["technology"], "sort": "New", "sinceHours": 48, "maxItems": 20 }
```

The week's discussions of a question community, only the ones that got answers:

```json
{ "mode": "communityPosts", "communities": ["asklemmy"], "sort": "TopWeek", "minComments": 5, "maxItems": 15 }
```

One keyword across every community the server knows:

```json
{ "mode": "searchPosts", "searchQuery": "docker", "sort": "TopMonth", "listingType": "all", "maxItems": 15 }
```

The same keyword in comments, newest first:

```json
{ "mode": "searchComments", "searchQuery": "kubernetes", "sort": "New", "maxItems": 15 }
```

A whole thread, two levels deep, highest-scoring branches first, comments only:

```json
{ "mode": "postComments", "postUrls": ["https://lemmy.world/post/920294"], "commentSort": "Top", "maxCommentDepth": 2, "includeOriginalPost": false, "maxItems": 20 }
```

Which communities exist for a topic, biggest first:

```json
{ "mode": "communities", "searchQuery": "climate", "sort": "TopAll", "maxItems": 15 }
```

The site-wide feed of the instance itself, for a dashboard:

```json
{ "mode": "communityPosts", "listingType": "local", "sort": "Active", "maxItems": 20 }
```

### Output

One real row of the cloud run `SYSbBnAaKNBhd6CbX` (input `{"mode": "communityPosts", "communities": ["technology"], "sort": "New", "sinceHours": 48, "excludeNsfw": true, "maxItems": 20}`):

```json
{
  "rowType": "post",
  "sourceMode": "communityPosts",
  "found": true,
  "notFoundReason": null,
  "postId": 52605462,
  "title": "Google rolls out new Gemini AI model but restricts access over safety concerns",
  "body": null,
  "postUrl": "https://www.theguardian.com/technology/2026/oct/01/google-releases-gemini-model-restrictions",
  "thumbnailUrl": "https://lemmy.world/pictrs/image/583dd762-f759-4431-901f-851b259e0901.jpeg",
  "embedTitle": "Google rolls out new Gemini AI model but restricts access over safety concerns",
  "embedDescription": "Tech company releases Gemini 4 Argon only to a vetted group of cybersecurity experts to avoid misuse by hackers",
  "community": "technology",
  "communityTitle": "Technology",
  "communityInstance": "lemmy.world",
  "authorHandle": "orwi@programming.dev",
  "authorDisplayName": null,
  "authorInstance": "programming.dev",
  "authorIsBot": false,
  "authorPostCount": null,
  "authorCommentCount": null,
  "score": -1,
  "upvotes": 2,
  "downvotes": 3,
  "commentCount": 2,
  "newestCommentAt": "2026-10-01T21:43:29.984Z",
  "createdAt": "2026-10-01T21:18:55.327Z",
  "updatedAt": null,
  "nsfw": false,
  "locked": false,
  "removed": false,
  "deleted": false,
  "featuredInCommunity": false,
  "featuredSiteWide": false,
  "language": "en",
  "matchedCommunity": "technology",
  "matchedQuery": null,
  "url": "https://lemmy.world/post/52605462",
  "apId": "https://programming.dev/post/57357662",
  "fetchedAt": "2026-10-01T22:28:58.531Z"
}
```

Download the dataset as JSON, CSV, Excel, HTML or XML, or read it over the API.

#### Post row (`rowType: "post"`)

| Field | Type | Meaning |
|---|---|---|
| `postId` | number | Id of the post **on lemmy.world**; the input of a `postComments` run |
| `title` | string | Post title as written |
| `body` | string | Post text, Markdown as the source stores it; `null` for a pure link post |
| `postUrl` | string | The link the post points at, `null` for a text post |
| `thumbnailUrl` | string | Preview image the server generated |
| `embedTitle`, `embedDescription` | string | Title and summary the server read from the linked page |
| `community`, `communityTitle` | string | Community name and its display name |
| `communityInstance` | string | Server hosting the community, e.g. `lemmy.world`, `slrpnk.net` |
| `authorHandle` | string | Author as `name@server`, always in that form |
| `authorDisplayName` | string | Chosen display name, often `null` |
| `authorInstance` | string | Server the author's account lives on |
| `authorIsBot` | boolean | The account declares itself a bot |
| `authorPostCount`, `authorCommentCount` | number | Lifetime totals of the account — filled in `userActivity`, `null` elsewhere |
| `score`, `upvotes`, `downvotes` | number | Public vote totals at `fetchedAt`; the score can be negative |
| `commentCount` | number | Comments the server counts under the post |
| `newestCommentAt` | string | UTC time of the newest comment |
| `createdAt`, `updatedAt` | string | Posted and last edited, UTC ISO-8601 |
| `nsfw` | boolean | Flagged by the post or by its community |
| `locked` | boolean | Closed for new comments |
| `removed`, `deleted` | boolean | Moderator removal / author deletion flags the source publishes |
| `featuredInCommunity`, `featuredSiteWide` | boolean | Pinned in the community or on the front page |
| `language` | string | Declared language code, `und` when the client set none |
| `matchedCommunity` | string | The community of the input that produced the row |
| `matchedQuery` | string | The keyword that produced the row |
| `url` | string | Permalink on lemmy.world |
| `apId` | string | Canonical link on the instance the post was written on |
| `found`, `notFoundReason` | boolean, string | `false` plus a reason when the source would not serve the input |
| `fetchedAt` | string | UTC time this row was read |

#### Comment row (`rowType: "comment"`)

| Field | Type | Meaning |
|---|---|---|
| `commentId` | number | Id of the comment on lemmy.world |
| `text` | string | Comment text, Markdown as written |
| `replyDepth` | number | 1 for a direct reply to the post, 2 for a reply to that, and so on |
| `parentCommentId` | number | The comment this one answers, `null` at depth 1 |
| `childCount` | number | Replies hanging below it — how much a depth or row limit cut off |
| `postId`, `postTitle` | number, string | The post the comment sits under |
| `community`, `communityTitle`, `communityInstance` | string | Where the discussion happens |
| `authorHandle`, `authorDisplayName`, `authorInstance`, `authorIsBot` | string, boolean | The comment's author |
| `score`, `upvotes`, `downvotes` | number | Public vote totals at `fetchedAt` |
| `createdAt`, `updatedAt` | string | Written and last edited, UTC |
| `removed`, `deleted`, `nsfw` | boolean | Moderation flags; `nsfw` is inherited from the post or community |
| `language` | string | Declared language code, often `und` |
| `matchedQuery`, `matchedCommunity` | string | What produced the row |
| `url`, `apId` | string | Permalink here and canonical link on the origin instance |
| `fetchedAt` | string | UTC time this row was read |

#### Community row (`rowType: "community"`)

| Field | Type | Meaning |
|---|---|---|
| `community`, `communityTitle` | string | Name and display name |
| `description` | string | Sidebar text, Markdown |
| `communityInstance` | string | Server hosting it |
| `subscribers`, `subscribersLocal` | number | Subscribers everywhere, and from lemmy.world alone |
| `postsCount`, `commentsCount` | number | Totals the server counts |
| `usersActiveDay`, `usersActiveWeek`, `usersActiveMonth`, `usersActiveHalfYear` | number | Accounts that posted or commented in the window |
| `moderatorCount` | number | Moderators — filled for communities you name, `null` for search and directory rows |
| `nsfw`, `postingRestrictedToMods` | boolean | Adult flag; only moderators may post |
| `createdAt`, `updatedAt` | string | Created and last changed, UTC |
| `icon` | string | Community icon URL |
| `matchedQuery` | string | The topic word that produced the row |
| `url`, `apId` | string | Link here and canonical link on the origin instance |
| `fetchedAt` | string | UTC time this row was read |

#### Views and the run summary

Three dataset views pick the columns worth a table: **Posts** (author, community and its server, title, score, comments, date, link), **Comments** (author, post, text, score, depth, replies below) and **Communities** (server, subscribers, active users, posts, comments, moderators). Every run also writes a `SUMMARY` record into its default key-value store with the request count, the rows per community, user or thread, the skipped rows per filter, the inputs the source would not serve and every warning — useful when a scheduled run returns fewer rows than expected.

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~lemmy-public-posts/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"searchComments","searchQuery":"kubernetes","sort":"New","maxItems":20,"fields":["authorHandle","community","postTitle","text","score","createdAt","url"]}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/lemmy-public-posts').call({ mode: 'communityPosts', communities: ['technology'], sinceHours: 24, maxItems: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) console.log(row.createdAt, row.authorHandle, row.score, row.title);
```

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
## 1) find the busiest post of a community, 2) read its discussion for a summary
feed = client.actor("yadroo/lemmy-public-posts").call(
    run_input={"mode": "communityPosts", "communities": ["asklemmy"], "sort": "TopWeek", "maxItems": 20}
)
rows = client.dataset(feed["defaultDatasetId"]).list_items().items
busiest = max(rows, key=lambda r: r.get("commentCount") or 0)
thread = client.actor("yadroo/lemmy-public-posts").call(
    run_input={"mode": "postComments", "postUrls": [busiest["url"]], "commentSort": "Top", "maxCommentDepth": 3, "maxItems": 100}
)
```

Chaining works in both directions: a `communities` run gives `community` and `communityInstance` values to paste straight into `communities` (`name@server`), and any post row gives a `url` for `postComments`. For a schedule, set `sinceHours` a little above the interval and `onlyNew: true` — an empty run then means nothing new was posted. Narrow rows with `fields` when an LLM reads them.

MCP: add `https://mcp.apify.com` to Claude, Cursor or any MCP client and call the `yadroo/lemmy-public-posts` tool with the same JSON input.

### Pricing

Pay per event: **$0.001 per run start + $0.0015 per dataset row**. Platform usage (compute and traffic) is included in these prices — nothing is charged on top. The start event is charged on every run, including a run that writes no row (a monitoring run with `onlyNew` and nothing new, for example). A `found: false` row — the one row that explains an unknown community, account or post — is a dataset row and is billed like any other; a post or comment the source serves twice in one run is written and billed once.

| Run | Rows | Cost |
|---|---|---|
| Community directory for a topic | 15 | $0.001 + 15 × $0.0015 = **$0.0235** |
| Community feed, last 24 h | 50 | $0.001 + 50 × $0.0015 = **$0.0760** |
| A busy thread, three levels deep | 200 | $0.001 + 200 × $0.0015 = **$0.3010** |

Store discounts lower the row price automatically: Bronze −10 %, Silver −20 %, Gold and above −30 %; the start event is the same on every plan. Set **Maximum cost per run** and the run stops exactly there: it writes the rows that budget pays for and ends with "Stopped at your spending limit: N rows delivered". Keep `maxItems` small while you try inputs out — it is the one input that decides what a run costs.

### Limits & FAQ

- **50 rows per request, so deep runs page.** The source refuses `limit` above 50 with an HTTP 400, which the actor clamps for you; a 1000-row run is roughly 20–25 requests and a few minutes.
- **Politeness and rate limits.** lemmy.world publishes its own per-IP limits (100 search calls per 10 minutes, 999 other reads per minute). The actor sends requests one at a time with a pause of ~350 ms, spaces searches after the first ten to one every 6 seconds, retries HTTP 5xx with backoff and honours `Retry-After`. Apify runs share outgoing IPs, so the search budget can already be partly used when a run starts: when the server refuses a request for its limit (HTTP 400 `rate_limit_error`, or 429), the actor waits 30 s, then 60 s, and if the refusal persists it stops reading and says so in the status message — the rows written so far are kept, a run that wrote nothing fails, and the next run should start about 10 minutes later. It never switches IPs to get around the limit. Only `/api/v3/*` of that one host is ever called, never the HTML pages.
- **Spending limit, timeout, restarts.** Rows are written page by page as they are read. The run stops at your maximum cost per run ("Stopped at your spending limit: N rows delivered"), stops starting new requests shortly before the run timeout and ends successfully with what it saved ("Stopped before the run timeout: N rows saved"), and a platform migration or restart never writes or bills a row twice.
- **`onlyNew` memory.** Rows are remembered as they are written, in a named key-value store of your account (`lemmy-seen`, or `lemmy-seen-<task id>` for a task, up to 50 000 ids), so a run cut short by `maxItems`, the spending limit or the timeout leaves the rest for the next run. A `found: false` row is remembered too: an unknown name gives one row once, and afterwards appears only in the status message.
- **A thread request is capped at 300 comments.** Asked for a depth limit, the source answers up to 300 comments of a post and ignores paging there; the actor then continues with the pageable form of the same endpoint to fill `maxItems`, and the status message says how many of the thread's comments it could read. `childCount` shows where a branch was cut.
- **No URL or domain search.** The source accepts a URL search type but answered zero posts for domain queries, so "who linked my site" is not offered rather than offered broken.
- **No phrase or boolean search.** `searchQuery` is one case-insensitive substring: no quotes, no `AND`/`OR`, no wildcards. For several phrases, run several tasks and merge the datasets.
- **`sinceHours` is applied here, not by the source.** There is no date parameter in the API. With `sort: New` the actor stops paging at the first row past the window; with any other order the window only filters what the chosen ranking already returned.
- **Most content declares no language.** `language` is `und` for a large share of rows. The `languages` filter can only keep what declares a language — add `und` to the list or leave the filter empty.
- **A federated view, not all of Lemmy.** Rows are what lemmy.world holds: content of instances it federates with, and nothing from instances it has defederated. `communityInstance` and `authorInstance` tell you where each row really comes from, and a post can exist elsewhere on the network without existing here.
- **Vote totals are a snapshot.** `score`, `upvotes` and `downvotes` are the values at `fetchedAt` and keep moving; votes are only ever read as the totals the server publishes, never per person.
- **Nothing behind a login, no contact harvesting.** Only what a signed-out visitor sees: posts, comments, pseudonymous handles, public vote totals, community counts and the post/comment totals a profile shows. No follower or subscription lists, no e-mail addresses, no chat identifiers — the source publishes a chat handle on some profiles and this actor deliberately does not write it. Images are linked, never downloaded.
- **Empty result vs. error.** A quiet community, a keyword nobody used or a filter that dropped everything is a successful run with the reason in the status message and in `SUMMARY`. An unknown community, account or post gives one row with `found: false` and a reason. A run that wrote nothing *and* hit a misbehaving source — or was stopped by the server's rate limit — fails with that reason, so a schedule can tell the two apart.
- **Deleted and removed content.** The API already leaves it out; the `removed` and `deleted` columns stay in the row shape for the cases where it still answers with a flag.
- **If the server closes these endpoints.** A Lemmy instance can put its API behind authentication. Should that happen here, this actor is marked paused on this page instead of working around a block.

***

Made by **Yadroo**. Sibling actors: [mastodon-public-posts](https://apify.com/yadroo/mastodon-public-posts), [bluesky-public-posts](https://apify.com/yadroo/bluesky-public-posts), [hackernews-search](https://apify.com/yadroo/hackernews-search), [stackexchange-search](https://apify.com/yadroo/stackexchange-search), [rss-to-json](https://apify.com/yadroo/rss-to-json).

# Actor input Schema

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

One question per run. `communityPosts`: the posts of the communities you name, or the site-wide feed when none is named. `searchPosts` / `searchComments`: one keyword across every community the server knows. `postComments`: the whole discussion under the posts you name, numbered by reply depth. `userActivity`: what named users posted and commented in public. `communities`: which communities exist for a topic, with their size and activity. Each mode fills one dataset view.

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

`name` for a community hosted on lemmy.world, `name@server` for one anywhere else (`programming@programming.dev`), or a community link. `communityPosts` splits the row limit evenly over them and writes the source community to `matchedCommunity`; leave empty for the site-wide feed. `searchPosts` and `searchComments` search inside the first one only. `communities` mode returns them with their counts and moderator count.

## `listingType` (type: `string`):

Applies when no community is named (the site-wide feed in `communityPosts`), to `searchPosts` and `searchComments`, and to the ranked list in `communities`. `all` is the federated view and the wider sample; `local` keeps only communities hosted on lemmy.world, which is what a study of that one community needs. Compare the result against the `communityInstance` column.

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

The order the source ranks posts in, used by `communityPosts`, `searchPosts`, `searchComments` and the ranked list in `communities`. `New` is the one to keep for a monitoring schedule: it is the only order in which a rolling window can stop paging early, so it is also the cheapest. The `Top*` orders are windows as well as orders - `TopWeek` can never return a post older than seven days, so do not combine them with a longer window and expect more rows.

## `searchQuery` (type: `string`):

What to search for in `searchPosts` (matched against post titles, bodies and posted links), in `searchComments` (comment text) and in `communities` (community names and titles). One phrase per run; the word that produced a row is written to `matchedQuery`. The source matches substrings case-insensitively and does not support quotes, AND/OR or wildcards - for several phrases, run several tasks and merge the datasets.

## `postUrls` (type: `array`):

Posts whose discussion you want, used by `postComments`. Accepted: a lemmy.world link (`lemmy.world/post/<id>`), the bare numeric id, or a post link on any other Lemmy instance (`programming.dev/post/<id>`) - a foreign link is first resolved to the copy this server holds, because every instance numbers posts differently. Every post row of any mode carries `postId` and `url`, so a feed or search run can be chained into a thread run. A deleted or unknown post yields one row with `found: false`.

## `includeOriginalPost` (type: `boolean`):

On by default: `postComments` writes one post row (`rowType: "post"`, no reply depth) before the comments of each thread, so the dataset carries the title, body, score and link the discussion is about. Switch off to get comments only - useful when you append many threads into one dataset for text analysis.

## `maxCommentDepth` (type: `number`):

Levels of replies to fetch in `postComments`: 1 is only the direct replies to the post, 8 covers almost every real thread. The source returns a whole subtree at once, so a deeper limit costs no extra requests but does cost rows - keep `maxItems` in mind on threads with hundreds of comments.

## `commentSort` (type: `string`):

Order of the comment branches in `postComments`. `Top` puts the branches the community agreed with first, which is what you want when `maxItems` cuts a long thread; `Old` reads the conversation the way it happened. The order applies inside every branch, the reply structure itself never changes.

## `users` (type: `array`):

Users whose public activity you want in `userActivity`, as `name` for a lemmy.world account, `name@server` for an account anywhere else, or as a profile link. The row limit is split evenly over them. Rows carry `authorPostCount` and `authorCommentCount`, the lifetime totals the source publishes for that account, so you can tell a one-post account from a prolific one without a second run. An unknown or deleted account yields one row with `found: false`.

## `userContent` (type: `string`):

What `userActivity` writes. `posts` is the account's own submissions, `comments` its replies in other people's threads - on Lemmy most accounts comment far more than they post, so `comments` is usually the bigger half. `both` splits the row limit evenly and mixes the two, newest first; the `rowType` column keeps them apart.

## `languages` (type: `array`):

Keep only posts or comments whose declared language is one of these, compared against the `language` column. Empty = every language. Read the honest part first: Lemmy stores a language per post, but most clients never set it, so a large share of real rows carry `und` (not set) rather than `en`. Pick `English` **and** `Not set (undetermined)` together if you want English-speaking communities without losing most of their posts.

## `authorInstances` (type: `array`):

Keep only rows whose author lives on one of these servers, for example `lemmy.world`, `lemm.ee`, `programming.dev`, `sh.itjust.works`. Compared against the `authorInstance` column. Empty = every server. This is the filter a federated network needs: watch one instance's voices inside a worldwide community, or drop an instance that floods a feed.

## `communityInstances` (type: `array`):

Keep only rows whose community is hosted on one of these servers, compared against the `communityInstance` column. Empty = every server. Useful for a site-wide or search run that should stay inside a known set of instances - a narrower form of the `local` listing, where you choose the servers yourself.

## `minScore` (type: `number`):

Keep only posts or comments whose score (upvotes minus downvotes) is at least this value. Empty = no floor. Use it to skip the long tail of a busy community and keep what people actually reacted to. Scores are a snapshot and keep moving, which is why every row carries `fetchedAt`; `upvotes` and `downvotes` are also written separately.

## `minComments` (type: `number`):

Keep only posts with at least this many comments. Empty = no floor. On a question-and-answer community this is the "got a real discussion" filter. Ignored for comment rows, which carry `childCount` instead.

## `excludeNsfw` (type: `boolean`):

On by default, because the site-wide feed and the community directory of an open network mix adult communities into every topic. Rows that survive carry `nsfw: false`. Switch it off for a complete sample - a moderation or research run usually needs one - and the `nsfw` column tells you which rows are flagged.

## `excludeBots` (type: `boolean`):

Lemmy news communities are fed by accounts that declare themselves bots, and the source publishes that flag. It is kept off by default so a feed export stays complete; every row carries `authorIsBot`, so you can filter later instead. Switch it on when the question is what humans wrote.

## `sinceHours` (type: `number`):

Rolling window in hours, counted in UTC from the start of the run: 24 for a day, 168 for a week. This is the input a schedule needs - no date to edit between runs. With `sort: New` paging stops as soon as the source returns older rows, so a narrow window is also a cheap run; with any other order the window only filters what was already fetched.

## `onlyNew` (type: `boolean`):

Remember the post and comment ids this run wrote (in a named key-value store of your account, one per task) and, on the next run of the same actor or task, write only what was not written before. Rows are remembered as they are written, so a run stopped by maxItems, your spending limit or the timeout leaves the rest for the next run. An empty run then means nothing new was posted.

## `maxItems` (type: `number`):

Hard cap on the rows this run writes, and therefore on what it costs. Paging stops as soon as the cap is reached. In `communityPosts`, `userActivity` and `postComments` the cap is divided over the communities, users or threads you named, so each one is represented. The run also stops at your maximum cost per run, with a status that says so.

## `fields` (type: `array`):

Keep only these columns, in the order given - for example `authorHandle, community, title, score, createdAt, url`. Empty = every column of the mode. `rowType`, `sourceMode`, `found` and `url` are always kept. Letter case is corrected; a name that is no column of the mode is reported in the status message with the nearest column; a list with no valid column fails the run before any row is written.

## Actor input object example

```json
{
  "mode": "communityPosts",
  "communities": [
    "technology",
    "asklemmy"
  ],
  "listingType": "all",
  "sort": "New",
  "searchQuery": "kubernetes",
  "postUrls": [
    "https://lemmy.world/post/920294"
  ],
  "includeOriginalPost": true,
  "maxCommentDepth": 8,
  "commentSort": "Top",
  "users": [
    "ruud@lemmy.world"
  ],
  "userContent": "posts",
  "excludeNsfw": true,
  "excludeBots": false,
  "onlyNew": false,
  "maxItems": 50
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "communities": [
        "technology",
        "asklemmy"
    ],
    "searchQuery": "kubernetes",
    "postUrls": [
        "https://lemmy.world/post/920294"
    ],
    "users": [
        "ruud@lemmy.world"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/lemmy-public-posts").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 = {
    "communities": [
        "technology",
        "asklemmy",
    ],
    "searchQuery": "kubernetes",
    "postUrls": ["https://lemmy.world/post/920294"],
    "users": ["ruud@lemmy.world"],
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/lemmy-public-posts").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 '{
  "communities": [
    "technology",
    "asklemmy"
  ],
  "searchQuery": "kubernetes",
  "postUrls": [
    "https://lemmy.world/post/920294"
  ],
  "users": [
    "ruud@lemmy.world"
  ]
}' |
apify call yadroo/lemmy-public-posts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/lemmy-public-posts"
        }
    }
}
```

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/5wTBonJmCfuSHkz0Y/builds/P3bs3SledbnH9BHGp/openapi.json
