# Pixelfed Creator Post Monitor - Public Feeds and Changes (`maydit/pixelfed-creator-post-monitor`) Actor

Monitor opt-in public Pixelfed creator feeds for captions, media links and saved post changes. Filter literal topics and dates. Bounded visible feed windows; no login or media downloads.

- **URL**: https://apify.com/maydit/pixelfed-creator-post-monitor.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (community)
- **Categories:** Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 public creator post observations

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

![Keep a public photo-post log — illustrative workflow](https://api.apify.com/v2/key-value-stores/YGxD9JRpduom8Q5Zd/records/pixelfed-creator-post-monitor-2e61b9219734.png?signature=1SmmAe5AOaqNbv4xcf6iM)

[Open this Actor in Console](https://console.apify.com/actors/93Kv9KcqIm9cAHTGM/input) · [Try a small run](#try-a-small-run) · [Cost](#cost)

**Mayd-It · Public photo feeds**

Turn opted-in Pixelfed creator feeds into a publishing log. Keep captions, source links and media metadata together, and notice changes visible in later feed responses.

**Source scope:** Explicit public Atom feeds with a small publisher-controlled window. Media links are returned; images and videos are not downloaded.

### What you get

- A distinct row for each public photo or photo-album post.
- Caption text, hashtags, source timestamps and supplied media-link metadata.
- Literal keyword evidence and optional saved caption or media changes.

### Try a small run

Copy this exact input into the Actor’s JSON input editor, then run it.

```json
{
  "feedUrls": [
    "https://pixelfed.social/users/pixelfed.atom"
  ],
  "maxPostsPerFeed": 5,
  "maxResults": 5
}
```

This example inspects up to five visible posts from the official Pixelfed creator feed. No account, API key or proxy is needed for this source. Open the dataset for rows and `SUMMARY` for actual coverage. `{}` inspects up to ten visible posts from the same feed, with a saved-result cap of 20; omitted or empty `feedUrls` and Console prefill use that feed. Comparison is off by default.

### Understand your run

Open **Run report** in the run’s Output tab for saved-record counts, reported source issues and limits, and suggested next steps. Use **CSV export** to take the saved results into a spreadsheet. The existing JSON results and detailed machine-readable summary remain available.

The report reflects this run’s recorded coverage. A successful run or a saved result does not establish that every possible source record was collected.

### Cost

**FREE / BRONZE: $0.002 per saved observation ($2 per 1,000).** SILVER: $1.60 per 1,000. GOLD / PLATINUM / DIAMOND: $1.20 per 1,000.

The Actor Start event is **$0.00005 per charged start unit**, with one unit per GB of Actor memory and a minimum of one. The Pricing tab shows the rate for your Apify tier. New, changed and ordinarily saved unchanged observations use the same result rate. Skipped unchanged rows, rejected events, duplicates and error diagnostics are not paid result rows. Use `onlyChanges` for repeated monitoring after establishing history.

### A real result

Selected fields from a recorded run on **10 October 2026**. This is an example of the output shape; source content and counters can change.

```json
{
  "creatorUsername": "pixelfed",
  "caption": "Stories. Shipping soon! #pixelfed",
  "updatedAt": "2026-09-21T15:09:22.000Z",
  "mediaCount": 3,
  "hashtags": [
    "pixelfed"
  ],
  "comparisonStatus": "not_requested"
}
```

`updatedAt` is the source Atom field. On the official template it contains the creation time, so it is not a reliable last-edit timestamp. `mediaCount` counts supplied links.

### More examples

#### Pixelfed founder public photo posts

Read the founder’s opted-in public photo feed.

```json
{
  "feedUrls": [
    "https://pixelfed.social/users/dansup.atom"
  ],
  "maxPostsPerFeed": 5,
  "maxResults": 5
}
```

#### Pixelfed creator topic watchlist

Inspect two creator feeds for the literal word `pixelfed`. These examples keep history off; add `onlyChanges: true` and a chosen `snapshotName` for a recurring monitor.

```json
{
  "feedUrls": [
    "https://pixelfed.social/users/pixelfed.atom",
    "https://pixelfed.social/users/dansup.atom"
  ],
  "keywords": [
    "pixelfed"
  ],
  "maxPostsPerFeed": 10,
  "maxResults": 20
}
```

***

Feed URLs must have the form `https://instance.example/users/username.atom`. Supply at most 20 explicit creators. Arbitrary RSS feeds, profile-page scraping and redirects to a different creator are unsupported.

### What the public source actually exposes

Pixelfed's [official controller](https://github.com/pixelfed/pixelfed/blob/dev/app/Http/Controllers/ProfileController.php) requires enabled Atom feeds, an unlocked local profile, and the creator's `show_atom` setting. Its current implementation selects the newest **10 public photo or photo-album posts** and caches a feed for **four hours**. Instances can run different versions or disable feeds. This Actor validates the expected feed, creator and post structure.

The feed window is not a complete archive. There is no pagination, whole-network search, private-content access, follower harvesting, image/video download, or inferred deletion detection. Increasing `maxPostsPerFeed` cannot expose history the publisher omits. Polling more often than the source cache refresh can repeatedly return the same content.

The [official Atom template](https://github.com/pixelfed/pixelfed/blob/dev/resources/views/atom/user.blade.php) currently places `created_at` in `<updated>`. Accordingly, **`updatedAt` is the literal Atom field, not a reliable last-edit timestamp**. The Actor compares caption and media snapshots to notice changes that become visible in later feed responses. `publishedAt` is null on the tested flagship feeds; it is populated only if a feed explicitly includes an Atom `<published>` timestamp.

### Filters and limits

| Input | Behavior |
|---|---|
| `feedUrls` | Up to 20 public HTTPS creator feed URLs; omitted/empty uses the official profile. |
| `keywords` | Up to 30 case-insensitive literal phrases, OR within the list, matched against title or caption. Empty matches all. |
| `updatedFrom`, `updatedTo` | Inclusive real `YYYY-MM-DD` UTC dates applied to Atom `updated`; these are not edit-date filters. |
| `maxPostsPerFeed` | Default 10, range 1–100; take the publisher's first usable entries **before** filters. |
| `maxResults` | Default 20, range 1–1,000; maximum confirmed saved rows across all feeds. |
| `maxRunSeconds` | Default 240, range 30–3,600; every network operation fits this and the platform deadline minus 25 seconds. |

Populated date and keyword filters combine with AND. Literal match evidence includes the matching field, phrase and excerpt. No semantic topic inference is performed. Output follows input feed order and then publisher entry order; it is not a global time sort. Duplicate feed URLs are fetched once and duplicate canonical post identities are saved once.

Captions are decoded into plain text and limited to 30,000 characters, with `captionTruncated` and a hash of the full decoded caption. Keyword matching inspects only the returned text. Media links are metadata only; nothing at those URLs is downloaded. Alt text and MIME types are null if the feed does not supply them. Email/contact elements and profile biographies are not collected.

### Output and history

`postId` is the canonical public post URL, including instance and creator; numeric IDs alone are not assumed globally unique. Each saved row includes:

- `postId`, `url`, `creatorUsername`, `creatorUrl`, `instanceUrl`, `feedUrl`, `feedTitle`, `feedUpdatedAt`;
- `title`, `caption`, `captionHash`, `captionTruncated`, `updatedAt`, optional `publishedAt`;
- `media` entries containing `url`, optional `mimeType` and optional `altText`; `mediaCount` and literal caption `hashtags`;
- `matchedKeywords`, `contentHash`, `observedAt`, and comparison fields.

Set `comparePrevious: true` to compare against a named key-value store. Set `onlyChanges: true` to enable comparison and skip unchanged observations. On the **first run**, every matching observation is emitted as `new`, and the run warns that it is establishing history. Here `new` means new to that saved history, not necessarily recently published. A later no-change run warns and succeeds with zero rows.

`comparisonStatus` is `not_requested`, `new`, `changed` or `unchanged`. `changedFields`, `previous` and `previousObservedAt` explain the comparison. The snapshot includes title, returned caption, full caption hash, truncation flag, source timestamps, ordered media metadata and sorted literal hashtags. Feed refresh timestamps and the Actor's observation time do not trigger post changes. Media URL changes do count, including an upstream URL rewrite.

History is scoped by `snapshotName`, the sorted feed set, literal filters and `maxPostsPerFeed`. Feed order and `maxResults` do not create a separate namespace. **Use one concurrent writer per scope.** An optimistic read-back detects an overlapping update, but the key-value store does not provide an atomic transaction. Choose a new snapshot name to reset or replace incompatible history.

Only confirmed saved observations advance history. Failed or spending-limit-rejected rows do not. An incomplete run can preserve accepted observations without establishing removals or a complete baseline. The store retains up to 20,000 identities per scope. `SUMMARY.historyUpdated` confirms whether a history commit occurred.

### Coverage and failures

The default key-value store's `SUMMARY` reports `rowsSaved`, `rowsObserved`, `filtered`, `unchangedSkipped`, `sourceCoverage`, `sourcesNotReached`, `failedSources`, `historyUpdated` and limit flags. `status: "ok"` means the requested bounded check completed, not that the full creator archive was covered. An ordinary requested result cap is not an error.

Time, source and spending interruptions set `incomplete: true`. Source errors are recorded in SUMMARY/logs, never as paid dataset rows. If all requested sources fail before any usable observation, the run fails. A valid feed with no visible posts or no matching filters warns and succeeds. A run stopped before its first save solely by a spending cap warns without pretending to have collected posts.

Requests honor instance robots rules, including matching `Crawl-delay`, across retries and redirects. A delay that cannot fit the remaining deadline stops that source. HTML masquerading as `robots.txt`, authentication blocks and unexpected feed shapes are rejected. Public DNS validation, pinned addresses and redirect checks prevent private-network access. XML DTDs/entities are rejected, and no linked media or external XML resources are fetched.

### Troubleshooting and support

| If you see… | Check this |
| --- | --- |
| A creator feed fails | Confirm that the creator has enabled Atom and that the profile is public and unlocked. A disabled feed does not mean the creator has no posts. |
| The same posts keep returning | The official feed can be cached for four hours and exposes a short window. A larger limit cannot recover omitted history. |
| A post appears unchanged | Compare the caption and media snapshot. The Atom timestamp alone does not establish whether a post was edited. |

For help, open an Actor issue or contact **bmay@mayd-it.com**. Include the run ID, selected sources and the relevant `SUMMARY` message. Do not send credentials. This Actor is independent of the platform.

Public availability does not transfer copyright or grant permission to republish creators’ images. This Actor returns public feed metadata and media links. Use the source information within the applicable instance and creator terms. It does not download media or build a training dataset.

# Actor input Schema

## `feedUrls` (type: `array`):

Up to 20 explicit HTTPS URLs in the form https://instance.example/users/username.atom. Empty or omitted uses the official Pixelfed profile. Feeds must be public and enabled by the creator. No credentials, query strings, private IPs or redirect to another profile. Instance robots rules and Crawl-delay apply.

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

Up to 30 case-insensitive literal phrases, OR within the list. Search only the exposed title and caption; empty matches all. Matching evidence is returned. No semantic, image-content or whole-network search.

## `updatedFrom` (type: `string`):

Optional inclusive YYYY-MM-DD in UTC, applied to the entry's Atom updated field. Current Pixelfed fills that field with created_at; this is not a reliable last-edit date.

## `updatedTo` (type: `string`):

Optional inclusive YYYY-MM-DD in UTC. Must be on or after updatedFrom. Applies only to the visible feed window.

## `maxPostsPerFeed` (type: `integer`):

Take this many usable posts in publisher order before keyword/date filtering. Current Pixelfed source exposes at most 10; increasing this cannot recover older posts. This is a requested window, not archive pagination.

## `maxResults` (type: `integer`):

Stop after this many confirmed saved rows across the supplied feeds, in feed input order and then publisher order. Unchanged skipped rows do not consume the cap. A chosen cap is a normal bounded run, not complete archive coverage.

## `comparePrevious` (type: `boolean`):

Use a named store scoped to this feed set, filters, window and snapshotName. First observations are returned as new and enter history only after confirmed dataset saves. Use one writer per scope; source feed caching can delay observed changes.

## `onlyChanges` (type: `boolean`):

Implies comparison. The first run emits matching observations and warns that it establishes history. Later unchanged observations are skipped. New means new to this saved history, not necessarily newly published. No deletion or disappearance inference.

## `snapshotName` (type: `string`):

1-80 letters, numbers, underscores or hyphens. Change it to establish a separate baseline. Existing saved history is preserved when a feed fails or rows cannot be saved.

## `maxRunSeconds` (type: `integer`):

Every request, retry and Crawl-delay fits this deadline and the platform deadline minus 25 seconds. SUMMARY reports partial coverage on time, source or spending limits.

## Actor input object example

```json
{
  "feedUrls": [
    "https://pixelfed.social/users/pixelfed.atom"
  ],
  "keywords": [],
  "maxPostsPerFeed": 10,
  "maxResults": 20,
  "comparePrevious": false,
  "onlyChanges": false,
  "snapshotName": "default",
  "maxRunSeconds": 240
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `runReport` (type: `string`):

Readable summary of saved records, reported source coverage, limits and next steps. Available when this run saves its SUMMARY.

## `csvExport` (type: `string`):

Download saved dataset records as a CSV file for spreadsheets and other tools.

## `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 = {
    "feedUrls": [
        "https://pixelfed.social/users/pixelfed.atom"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/pixelfed-creator-post-monitor").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 = {
    "feedUrls": ["https://pixelfed.social/users/pixelfed.atom"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("maydit/pixelfed-creator-post-monitor").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 '{
  "feedUrls": [
    "https://pixelfed.social/users/pixelfed.atom"
  ],
  "maxResults": 20
}' |
apify call maydit/pixelfed-creator-post-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maydit/pixelfed-creator-post-monitor"
        }
    }
}
```

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/93Kv9KcqIm9cAHTGM/builds/4Y2iXW5XdQsDcSv1h/openapi.json
