# Feed Quality Monitor: RSS, Atom & JSON QA (`sassy_labs/feed-quality-monitor`) Actor

Audit up to 20 public RSS, Atom, RDF/RSS, or JSON Feed endpoints. Detect broken delivery, malformed feeds, stale content, missing fields, duplicates, invalid URLs and dates, and entry-limit truncation; export normalized entries plus reusable ETag and Last-Modified state.

- **URL**: https://apify.com/sassy\_labs/feed-quality-monitor.md
- **Developed by:** [Sassy Labs](https://apify.com/sassy_labs) (community)
- **Categories:** News, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 feed auditeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Feed Quality Monitor

**Feed Quality Monitor** is a deterministic RSS feed validator and parser API
for RSS, Atom, RDF/RSS, and JSON Feed production pipelines. Add 1–20 public
feed URLs and get explicit `pass`, `fail`, or `unknown` health receipts for
delivery, parsing, required fields, dates, freshness, duplicates, and entry
limits—plus normalized entries and reusable ETag/Last-Modified state.

Use it to catch a feed that is reachable but stale, structurally broken,
missing required data, duplicating items, or returning inconclusive evidence.
It reads only the public feed endpoint: it does not crawl linked articles,
discover feeds from websites, bypass access controls, or ask a language model
to guess missing values. Schedule repeated health checks when useful, but note
that v0.2 returns cache validators rather than retaining prior entries or
claiming stateful change detection.

Live Store listing:
https://apify.com/sassy_labs/feed-quality-monitor

### What v0.2 checks

For each feed:

- public-only HTTP delivery, final status, and redirect count;
- redirect destination revalidation before every request;
- recognized feed media types, while still allowing a valid feed served under
  a generic type;
- well-formed XML or JSON and a supported RSS, Atom, RDF/RSS, or JSON Feed
  structure;
- required feed metadata;
- invalid metadata URLs and dates;
- a bounded maximum entry count;
- duplicate entries by stable source ID, normalized URL, then deterministic
  content hash;
- required entry fields;
- invalid entry URLs and dates;
- newest declared feed or entry date against `staleAfterHours`; and
- ETag, Last-Modified, conditional-request, and response-cache behavior.

Long summary and content values are represented by bounded previews. Any
preview truncation is named in `truncatedFields` and in a non-blocking
`output_truncation` receipt. Entries beyond `maxEntriesPerFeed` are not hidden:
the feed fails `entry_limit` and reports the exact omitted count.

### Example input

```json
{
  "feeds": [
    {
      "url": "https://www.w3.org/blog/news/feed/"
    },
    {
      "url": "https://example.com/feed.json",
      "etag": "W/\"prior-etag\"",
      "lastModified": "Wed, 15 Jul 2026 12:00:00 GMT"
    }
  ],
  "timeoutSeconds": 20,
  "maxRedirects": 5,
  "maxFeedBytes": 5000000,
  "maxEntriesPerFeed": 500,
  "staleAfterHours": 168,
  "concurrency": 4,
  "requiredFeedFields": [
    "title"
  ],
  "requiredEntryFields": [
    "title",
    "url",
    "date"
  ]
}
````

One to twenty feed targets are accepted. Duplicate target URLs and unsupported
input fields are rejected rather than merged silently.

### Current pay-per-feed billing

The live Store listing uses pure pay-per-event pricing:

- `apify-actor-start`: the platform-managed synthetic start event at
  `$0.00005`; Actor code never charges it;
- `feed-audited`: one custom `$0.01` event charged after complete input
  validation and immediately before each feed request; and
- `apify-default-dataset-item`: disabled, because one feed can produce hundreds
  of diagnostic records without creating hundreds of units of buyer value.

Platform usage is included in the event price
(`platformUsagePassedToUser=false`). Event charges are therefore `$0.01005`
for one feed, `$0.05005` for five feeds, and `$0.20005` for twenty feeds.

Every `ChargeResult` is authoritative. If `chargedCount` is zero or the event
budget is exhausted, that feed is not requested. It still receives one
`budget_limit` feed record with `status: "unknown"` and `audited: false`.
The overview separately reports `requestedFeeds`, `processedFeeds`, and
`budgetLimitedFeeds`. Paid feed requests can run concurrently, but charge gates
are serialized so a tight `ACTOR_MAX_TOTAL_CHARGE_USD` cannot authorize two
feeds at once.

The public configuration was verified on 17 July 2026 with `feed-audited` as
the primary event, platform usage included, limited permissions, hidden source
files, Standby disabled, 512 MB minimum/default memory, and a 1024 MB maximum.
Owner cloud preflights for 1, 5, and 20 feeds reported total platform usage of
approximately USD 0.00034, USD 0.00093, and USD 0.00294. Those runs are cost
and reliability evidence, not customer demand or revenue.

### Conditional requests

After a run, the default key-value store record `CACHE_STATE` contains final
feed URLs and any returned ETag or Last-Modified values:

```json
[
  {
    "url": "https://www.w3.org/news/feed/",
    "etag": "\"example-etag\"",
    "lastModified": null
  }
]
```

Use those objects as the next run's `feeds` value. A validator is sent only on
the first request to that exact URL. It is deliberately dropped after a
redirect so an opaque ETag or Last-Modified value cannot leak to another
origin. Because the returned state uses the final URL, later runs can normally
avoid the redirect and make a useful conditional request directly.

An HTTP 304 proves that the conditional transport path worked, but it does not
provide a body. v0.2 therefore marks parsing and freshness `unknown` instead of
reconstructing or inventing prior content. The returned validators are
preserved for the next run.

### Output contract

Dataset records are ordered:

1. exactly one `overview`;
2. one `feed` record for each requested target, in input order; and
3. that feed's unique `entry` records, in source order.

The `overview` dataset view is optimized for record type, status, format,
summary, URLs, HTTP status, dedupe method, duplicate count, and check time.
Start with the overview, then scan each feed and normalized entry. `CACHE_STATE`
separately returns reusable ETag and Last-Modified validators.

Each feed and entry record contains a `receipts` array. Every receipt names:

- a stable check ID;
- `pass`, `fail`, or `unknown`;
- severity;
- whether it affects the parent record's status;
- expected behavior;
- observed evidence; and
- a small evidence array.

Normalized entry output includes source ID, title, absolute fragment-free HTTP
URL, external URL, bounded summary and content preview, SHA-256 content hash,
published and modified dates, raw date strings, authors, tags, dedupe method,
dedupe key, and duplicate occurrences.

No missing title, URL, date, author, tag, or feed field is synthesized. Relative
HTTP links are resolved deterministically against the final feed URL; invalid
or unsupported links remain `null` with a failing receipt.

### Concrete example output

This abridged output came from the verified 17 July 2026
`normalize-rss-atom-json-feed-structured-entries` example. It read one RSS 2.0
feed, one Atom feed, and one JSON Feed, then normalized 83 unique entries.

```json
{
  "recordType": "overview",
  "status": "pass",
  "summary": "Feed quality passed for 3 feed(s)",
  "totals": {
    "requestedFeeds": 3,
    "processedFeeds": 3,
    "budgetLimitedFeeds": 0,
    "entries": 83,
    "duplicatesSuppressed": 0,
    "feedPass": 3,
    "feedFail": 0,
    "feedUnknown": 0
  },
  "feedStatuses": [
    {
      "status": "pass",
      "format": "rss2"
    },
    {
      "status": "pass",
      "format": "atom"
    },
    {
      "status": "pass",
      "format": "json_feed"
    }
  ]
}
```

The same run returned one reusable cache-state object per feed:

```json
[
  {
    "url": "https://www.w3.org/news/feed/",
    "etag": "\"5cb9491929bf42153cf24adfaba8c052-gzip\"",
    "lastModified": null
  }
]
```

Response dates, ETags, entry counts, and content naturally change as publishers
update their feeds; this example records the observed run rather than promising
fixed values.

### Safety boundary

- Only public HTTP(S) URLs on ports 80 and 443 are accepted.
- URL credentials, localhost, internal hostnames, and private, reserved,
  link-local, multicast, and otherwise non-public IP addresses are rejected.
- Every DNS answer must be public; mixed public/private answers are rejected.
- The network socket performs its own public-address check, limiting DNS
  rebinding between validation and connection.
- Every redirect destination is revalidated, and conditional validators are
  dropped after the initial URL.
- Response bodies are streamed through a hard decoded-byte limit.
- XML `DOCTYPE` and custom entity declarations are rejected. External entities
  are never resolved.
- Runs are bounded to twenty feeds, eight concurrent requests, ten redirects,
  ten megabytes per feed, and five thousand entries per feed.
- No login, cookie, API key, proxy subscription, customer credential, scraping
  bypass, browser automation, or model call is accepted or required.

### What it does not claim

This is a deterministic feed-health and normalization check, not a full
conformance certification for every RSS extension, a semantic fact checker,
an HTML sanitizer, a malware scanner, or proof that subscribers consumed an
entry. It does not fetch entry pages, discover feeds from websites, follow
pagination hubs, preserve an internal content cache, or mutate a target.

Namespaced RSS fields commonly used for content, creators, dates, and Atom
self-links are recognized. Unsupported extension fields remain absent rather
than being guessed.

### Local verification

Prerequisites: Node.js 24.11+.

```powershell
npm ci
npm run verify
npx --yes apify-cli@1.7.1 validate-schema
npm audit --omit=dev
```

The tests inject network fixtures and cover:

- RSS, Atom, and JSON Feed parsing;
- malformed XML and JSON;
- XML custom-entity rejection;
- private, mixed-DNS, credential, port, and redirect blocking;
- conditional-header isolation across redirects;
- decoded response-size limits;
- deterministic dedupe;
- required-field, invalid-date, invalid-URL, stale-feed, empty-body, and entry
  limit behavior;
- HTTP 304 unknown semantics;
- ordered dataset and cache-state writes;
- zero-charge, exhausted-budget, and concurrent one-event PPE gates; and
- release, Docker, memory, pricing-plan, and dataset-view metadata.

#### Local pay-per-event boundary check

Apify's local PPE test mode assigns USD 1 to a custom event that is not yet
configured in Console. A USD 1 cap therefore permits exactly one
`feed-audited` event. The second feed must remain unrequested and appear as an
explicit budget-limited record:

```powershell
$env:ACTOR_TEST_PAY_PER_EVENT = "true"
$env:ACTOR_USE_CHARGING_LOG_DATASET = "true"
$env:ACTOR_MAX_TOTAL_CHARGE_USD = "1"
npx --yes apify-cli@1.7.1 run --purge --input-file demo/ppe-input.json
npm run verify-ppe-log
Remove-Item Env:ACTOR_TEST_PAY_PER_EVENT
Remove-Item Env:ACTOR_USE_CHARGING_LOG_DATASET
Remove-Item Env:ACTOR_MAX_TOTAL_CHARGE_USD
```

The verifier requires exactly one charging-log record named `feed-audited`,
one processed feed, one unaudited `budget_limit` feed, and no manually charged
start or dataset-item event.

### Live demo

```powershell
npm run demo
```

The demo performs a read-only request to the public W3C News RSS feed at
`https://www.w3.org/blog/news/feed/`, writes ignored local artifacts under
`output/`, and prints the overview. The verification run on 17 July 2026
followed the W3C redirect to `https://www.w3.org/news/feed/`, parsed RSS 2.0,
emitted 25 unique entry records, found zero duplicates, returned a reusable
ETag, and passed every configured health check. A second live request using
that returned final URL and ETag received HTTP 304; transport passed while
parse and freshness correctly remained `unknown`.

The public Actor requires 512–1024 MB. Its `.actor/Dockerfile` uses the Node.js 24
Apify image, matches the package's Node 24 engine and `npm start` command, and
the root `.dockerignore` excludes dependencies, local storage, live demo
output, environment files, logs, coverage, tests, and deployment notes.

The Actor is published at
https://apify.com/sassy\_labs/feed-quality-monitor. The task-example and icon
optimization files in `deployment/` and `assets/` remain local until a
separate reviewed publication is approved.

# Actor input Schema

## `feeds` (type: `array`):

One to twenty public feed URLs. Paste the returned cache state into a later run to make a conditional request. Validators are sent only to the exact requested URL and are dropped on redirects.

## `timeoutSeconds` (type: `integer`):

Hard network timeout per redirect hop.

## `maxRedirects` (type: `integer`):

Every redirect destination is DNS-resolved and public-network validated before it is requested.

## `maxFeedBytes` (type: `integer`):

Stops a decoded response stream before it can consume unbounded memory.

## `maxEntriesPerFeed` (type: `integer`):

Entries beyond this limit are explicitly reported as truncated and fail the feed health receipt.

## `staleAfterHours` (type: `integer`):

Fail when the newest valid entry or feed date is older than this threshold. Missing date evidence is unknown, never guessed.

## `concurrency` (type: `integer`):

Maximum simultaneous bounded feed requests.

## `requiredFeedFields` (type: `array`):

Missing selected feed-level fields fail the feed. Supported values are title and homePageUrl.

## `requiredEntryFields` (type: `array`):

Missing selected normalized entry fields fail that entry and its parent feed.

## Actor input object example

```json
{
  "feeds": [
    {
      "url": "https://www.w3.org/blog/news/feed/"
    }
  ],
  "timeoutSeconds": 20,
  "maxRedirects": 5,
  "maxFeedBytes": 5000000,
  "maxEntriesPerFeed": 500,
  "staleAfterHours": 168,
  "concurrency": 4,
  "requiredFeedFields": [
    "title"
  ],
  "requiredEntryFields": [
    "title",
    "url",
    "date"
  ]
}
```

# Actor output Schema

## `overview` (type: `string`):

Overall status, requested/processed/budget-limited counts, billing evidence, feed statuses, and applied settings.

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

The first dataset item is the overview. Remaining items are audited feed records, normalized entries, or explicit budget\_limit feed records with audited=false.

## `cacheState` (type: `string`):

Final feed URLs with ETag and Last-Modified values suitable for a later conditional run.

# 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 = {
    "feeds": [
        {
            "url": "https://www.w3.org/blog/news/feed/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sassy_labs/feed-quality-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 = { "feeds": [{ "url": "https://www.w3.org/blog/news/feed/" }] }

# Run the Actor and wait for it to finish
run = client.actor("sassy_labs/feed-quality-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "feeds": [
    {
      "url": "https://www.w3.org/blog/news/feed/"
    }
  ]
}' |
apify call sassy_labs/feed-quality-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=sassy_labs/feed-quality-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Feed Quality Monitor: RSS, Atom & JSON QA",
        "description": "Audit up to 20 public RSS, Atom, RDF/RSS, or JSON Feed endpoints. Detect broken delivery, malformed feeds, stale content, missing fields, duplicates, invalid URLs and dates, and entry-limit truncation; export normalized entries plus reusable ETag and Last-Modified state.",
        "version": "0.2",
        "x-build-id": "q4D87qQouAF5afdn3"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/sassy_labs~feed-quality-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-sassy_labs-feed-quality-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/sassy_labs~feed-quality-monitor/runs": {
            "post": {
                "operationId": "runs-sync-sassy_labs-feed-quality-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/sassy_labs~feed-quality-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-sassy_labs-feed-quality-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "feeds"
                ],
                "properties": {
                    "feeds": {
                        "title": "Feed targets",
                        "minItems": 1,
                        "maxItems": 20,
                        "type": "array",
                        "description": "One to twenty public feed URLs. Paste the returned cache state into a later run to make a conditional request. Validators are sent only to the exact requested URL and are dropped on redirects.",
                        "items": {
                            "type": "object",
                            "additionalProperties": false,
                            "properties": {
                                "url": {
                                    "title": "Public feed URL",
                                    "description": "Public HTTP(S) RSS, Atom, RDF/RSS, or JSON Feed endpoint on port 80 or 443.",
                                    "type": "string",
                                    "editor": "textfield",
                                    "minLength": 8,
                                    "maxLength": 2048
                                },
                                "etag": {
                                    "title": "Prior ETag",
                                    "description": "Optional ETag returned by an earlier run. It is sent as If-None-Match only to this exact requested URL.",
                                    "type": "string",
                                    "editor": "textfield",
                                    "maxLength": 512
                                },
                                "lastModified": {
                                    "title": "Prior Last-Modified",
                                    "description": "Optional HTTP date returned by an earlier run. It is sent as If-Modified-Since only to this exact requested URL.",
                                    "type": "string",
                                    "editor": "textfield",
                                    "maxLength": 256
                                }
                            },
                            "required": [
                                "url"
                            ]
                        }
                    },
                    "timeoutSeconds": {
                        "title": "Timeout per request",
                        "minimum": 3,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Hard network timeout per redirect hop.",
                        "default": 20
                    },
                    "maxRedirects": {
                        "title": "Maximum redirects",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Every redirect destination is DNS-resolved and public-network validated before it is requested.",
                        "default": 5
                    },
                    "maxFeedBytes": {
                        "title": "Maximum feed bytes",
                        "minimum": 10000,
                        "maximum": 10000000,
                        "type": "integer",
                        "description": "Stops a decoded response stream before it can consume unbounded memory.",
                        "default": 5000000
                    },
                    "maxEntriesPerFeed": {
                        "title": "Maximum entries per feed",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Entries beyond this limit are explicitly reported as truncated and fail the feed health receipt.",
                        "default": 500
                    },
                    "staleAfterHours": {
                        "title": "Stale after",
                        "minimum": 1,
                        "maximum": 8760,
                        "type": "integer",
                        "description": "Fail when the newest valid entry or feed date is older than this threshold. Missing date evidence is unknown, never guessed.",
                        "default": 168
                    },
                    "concurrency": {
                        "title": "Concurrent feed requests",
                        "minimum": 1,
                        "maximum": 8,
                        "type": "integer",
                        "description": "Maximum simultaneous bounded feed requests.",
                        "default": 4
                    },
                    "requiredFeedFields": {
                        "title": "Required feed fields",
                        "maxItems": 2,
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Missing selected feed-level fields fail the feed. Supported values are title and homePageUrl.",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "title"
                        ]
                    },
                    "requiredEntryFields": {
                        "title": "Required entry fields",
                        "maxItems": 3,
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Missing selected normalized entry fields fail that entry and its parent feed.",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "title",
                            "url",
                            "date"
                        ]
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
