# Live Citation Passage Check (`remarkable_whipscorpion/citation-passage-check`) Actor

Check short quotes against currently fetched static HTML passages. Batch row references, scoped lexical results and safe unknowns.

- **URL**: https://apify.com/remarkable_whipscorpion/citation-passage-check.md
- **Developed by:** [Kagerou AP](https://apify.com/remarkable_whipscorpion) (community)
- **Stats:** 2 total users, 1 monthly users, 86.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 citation checkeds

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/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

## Live Citation Passage Check

Check a batch of short quotations against the static HTML text currently returned by known, authorized URLs. Receive ordered JSON rows for delivery QA in n8n, Make or an API workflow: `exact`, `whitespace-normalized`, `not_observed_in_extracted_text`, or `unknown`.

This is a case-sensitive lexical observation within a defined extracted region. It does not verify truth, semantic entailment, historical versions, rendered visibility, or absence from the whole source. A quote outside the extracted region may still exist on the page. Browser rendering, login, CAPTCHA bypass, proxies, paid fallback, crawling links and source full-text output are outside the service.

The packaging hypothesis is one bounded batch call and a consistent row contract, reducing HTTP, extraction and QA wiring. Extra purchase demand, retention and profitability have not been measured. Teams already satisfied with their existing retrieval/script may get little added value.

### Input

```json
{
  "authorizedToFetch": true,
  "allowedOrigins": ["https://your-authorized-source.example"],
  "rows": [
    {"rowId": "report-17", "url": "https://your-authorized-source.example/report", "quote": "A short passage to check."}
  ]
}
```

Supply at most 100 rows. `rowId` is a nonempty string up to 128 JavaScript UTF-16 code units. `quote` is nonempty and at most 300 Unicode code points; do not HTML-encode the supplied quote. `allowedOrigins` contains exact HTTP(S) origins with no path, credentials or trailing slash. Confirm that you have permission under source terms to fetch the URLs. Robots permission alone is not source authorization. Unsupported row fields are ignored. Invalid rows return `unknown` without a custom event; an invalid batch envelope fails the run. Duplicate IDs remain intact and `rowIndex` distinguishes their positions.

The fixed UI prefill and Actor example run input use one public MDN documentation row: origin `https://developer.mozilla.org`, URL `https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main`, and quote `The main content area consists of content` (`rowId: mdn-main-demo`). The prefilled authorization is specific to this demo. Its default remains false, and explicit custom JSON with omitted/false authorization is rejected. A run with no explicit input uses the configured demo example input. The MDN page is live and may change or return unknown under source-safety rules.

Demo quote attribution: [MDN Web Docs, Mozilla Contributors, `<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main), [CC BY-SA 2.5 or later](https://creativecommons.org/licenses/by-sa/2.5/), according to [MDN's documentation license](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Attrib_copyright_license). The quotation is unchanged; this demo selects only a short excerpt. The quoted material retains that license.

Only public IPv4 and public IPv6 within the global unicast allocation are allowed. Private/reserved/documentation addresses, including newer `3fff::/20`, are denied in literals, all DNS answers and redirects. The pinned parser's classification is supplemented by the [IANA IPv6 special registry](https://www.iana.org/assignments/iana-ipv6-special-registry). Connections use one validated DNS address, retain the original hostname for TLS certificate verification, and verify the socket peer. Every redirect is checked against public-address and declared-origin restrictions. Only standard HTTP/HTTPS ports are supported.

### Extraction and matching contract

`extractionScope` is `single-main-or-article-static-dom-text-v1`. The parser decodes HTML entities, finds exactly one nonexcluded `<main>`/`role="main"`, or exactly one `<article>` if there is no main, and concatenates its static DOM text. Block tags add newlines; the outer string is trimmed. Script, style, noscript, navigation, header/footer, aside, form/button, SVG, template and iframe subtrees are omitted. Elements with `hidden`, `inert`, `aria-hidden="true"`, supported inline `display:none`/`visibility:hidden`, or navigation/banner/contentinfo/complementary roles are omitted. Closed dialogs and closed details content other than summary are excluded. At least one nonempty paragraph/pre/blockquote/list-item/table-cell and reasonable non-link content are required.

Ordinary CSS and analytics scripts are accepted when a clear static body is present. External CSS, computed style, media queries and script mutations are not evaluated, so the scope is DOM extraction, not a screenshot or accessibility-tree assertion. Loading skeletons, empty/navigation-only content, ambiguous regions, short embedded-content shells, detected gates/challenges, unsupported MIME/encoding and failed retrieval return `unknown`. Conservative detection can yield unknown for an otherwise usable page. Only HTTP200 UTF-8 `text/html`, declared in the HTTP header or a UTF-8 meta declaration, is supported. PDFs, plain text, XHTML and content without a supported body region return unknown.

`exact` uses the supplied literal quote against extracted text. `whitespace-normalized` replaces ECMAScript `\s+` with U+0020 and trims both quote and text. Both are case sensitive and do not normalize Unicode, punctuation or accents. `not_observed_in_extracted_text` means that neither comparison matched in that successfully extracted text. It is a completed check, not a source-wide absence claim.

`offset` is the first match's zero-based JavaScript UTF-16 code-unit index. `offsetText` identifies `extractedText` or `whitespaceNormalizedExtractedText`; offsets are never positions in source HTML or rendered pixels. No-match/unknown offsets are null. `contentHash` is SHA-256 of exact extracted text encoded as UTF-8. The fetched HTML and extracted strings stay in memory and are never saved, returned or routinely logged.

### Output and examples

Each row returns `rowIndex`, `rowId`, `requestedUrl`, `finalUrl`, `observedAt`, `status`, `reason`, `matchMode`, `offset`, `offsetText`, `offsetUnit`, `normalization`, `extractionScope`, `contentHash`, `evaluationId` and `customChargedCount`. `observedAt` is the current retrieval observation time, not publication time. Unknowns can retain a final URL/time when HTTP was observed but text inspection could not complete.

The authoritative KVS record `OUTPUT` contains ordered `results`, per-evaluation billing state, confirmed `chargedUnits`, `chargedUnitsExact`, `stopReason` and `completed`. The final dataset projects the rows for normal JSON/CSV consumption. A crash or platform abort can leave only the accessible KVS partial record. A saved unfinished record is returned on restart without replaying work, payment or dataset writes; start a new run for a fresh observation. If final dataset publication fails, use the KVS output. No charge transaction is assumed atomic with storage.

`customChargedCount` is 0 or 1 for the actual row that owns an evaluation. Duplicate rows carry `duplicateOfRowIndex` and `evaluationChargedCount` while their own count is0. Pending/lost charge responses use null counts and `chargedUnitsExact:false`; confirmed `chargedUnits` is then a lower bound, not proof of zero charges. The Actor never retries an uncertain charge.

`sample-input.json` and `sample-output.json` are synthetic fixtures, with no network or actual payment. `npm run sample` yields five rows in input order: exact, whitespace-normalized, scoped no-match, duplicate exact, unknown. Only three unique resolved evaluations simulate a custom event. The reserved sample host is not a live demo endpoint. Replace it with an authorized source for a real run.

```json
{
  "rowIndex": 2,
  "rowId": "review",
  "status": "not_observed_in_extracted_text",
  "reason": "no_lexical_match_in_scope",
  "matchMode": "none",
  "offset": null,
  "offsetText": "extractedText",
  "customChargedCount": 1
}
```

This excerpt omits the common metadata; complete sample files contain it. A live MDN short-quote test passed real DNS/pinned TLS/robots/body retrieval and exact matching locally. That is not an Apify cloud or payment test.

### Price and budget behavior

Proposed Store price: **$0.01 per unique resolved citation evaluation** (`citation_checked`). Exact, whitespace-normalized and scoped no-match are billable. Empty input, invalid rows and unknown evaluations cost **$0 in custom events**. Identity is the canonical requested URL with fragment removed plus the literal supplied quote, within this run. Different quotes or different requested URL aliases are distinct evaluations; duplicate row references remain. Same-URL rows share one fetched snapshot.

Apify automatically adds `apify-actor-start`, currently **$0.00005 per start at this Actor's 256MB memory**. It still applies to empty/error/unknown-only runs. The Actor never manually charges it. The publisher must remove `apify-default-dataset-item`; otherwise the runtime pricing guard refuses to execute. Results are saved before a serial custom `charge(count:1)` and dataset publication uses a direct uncharged SDK Dataset write. No unit also uses a charged push.

SDK3.7.2 may round remaining capacity and intentionally overcharge one unit on an over-limit request. The wrapper checks capacity before work and immediately before charge, adds a strict decimal microUSD guard (budget rounded down, prices up), reserves the native start, checks actual `chargedCount`, and preserves the last paid result even when the limit is reached. Unfunded subsequent rows become unknown. Explicit zero budget is honored despite the SDK's zero-to-Infinity coercion. Local SDK PPE tests use its synthetic **$1** event price and cannot prove the Store's $0.01 price.

One checked row is nominally $0.01005 including start, 100 distinct resolved rows $1.00005; discounts/refunds or future platform changes are outside these examples. Set `maxTotalChargeUsd` in native run options, not Actor input. A per-run customer event limit does not enforce the creator's cumulative loss cap.

Under PPE-only pricing, the creator bears applicable paid-user compute/storage/transfer costs and receives the eligible revenue share after the platform fee. Free-plan usage does not establish eligible creator revenue. No upstream paid API/model is used by this Actor. Price and margin are untested hypotheses; cloud cost, payment activation and KYC status are unverified. [Official PPE and cost documentation](https://docs.apify.com/actors/publishing/monetize/pay-per-event), [pricing and costs](https://docs.apify.com/actors/publishing/monetize/pricing-and-costs).

### Bounds and failure behavior

- 100 rows, 300 quote code points, 2MiB per URL operation including robots/redirect bodies, 20MiB total body bytes per run.
- Request/DNS timeout5sec, HTTP work deadline60sec, at most3 redirects, hard concurrency cap4; this minimal implementation processes evaluations sequentially (actual concurrency1).
- `Accept-Encoding: identity`; any compressed response is rejected before decompression. No compressed expansion is processed. Headers are capped at16KiB. Byte caps apply to streamed response bodies, not TLS/header overhead; an already delivered final network chunk can cross the cap before immediate abort.
- Robots are checked per origin and cached. Robots404/410 means no rule file; inaccessible/malformed/unsupported robots is unknown. Redirected robots files fail closed. Declared positive crawl delays fail closed rather than being ignored.
- HTTP429 stops all pending/future HTTP work; no retry or fallback. Deadline, total-byte stop, source restriction, DNS/peer failures and request timeout produce explicit unknowns. No credentials, cookies or authorization headers are sent to sources.
- The native publisher/caller must set the run timeout60sec to bound SDK/platform I/O as well as internal HTTP work. Forced abort may leave recoverable partial KVS output.

### Local verification and deployment

Node24.22.1 is the pinned runtime. Dependencies are pinned by package and lockfile: Apify3.7.2 (Apache-2.0), htmlparser2 10.0.0 (MIT), ipaddr.js2.2.0 (MIT), robots-parser3.0.1 (MIT). Preserve bundled dependency licenses. SDK transitive dependencies are locked; no browser or extra app framework is installed. The minimal HTTP component was copied from the completed KB implementation and adapted/tested here; it is not a shared framework.

```text
npm ci --ignore-scripts
npm test
npm run sample
node test/public-smoke.js
```

Use `npm.cmd` on Windows. SDK memory probes/test subprocesses may need the existing approved execution route; failed or skipped environment tests are not passes. `test/public-smoke.js` performs one explicitly selected public HTTPS observation and saves only metadata/short quote under the implementation report folder; it is separate from the deterministic test suite. Keep `storage`, npm cache and node_modules out of deployment.

Publisher checklist (configuration files alone do not prove completion):

1. Obtain independent QA, simplification and market/TOS approval for the frozen manifest. Use the existing authenticated Console Web IDE or normal CLI upload path. Owner authentication/billing/KYC/terms stay with the authorized publisher/owner; no token paste is required by this product.
2. Upload/build privately, with Node24 Dockerfile, 256MB memory,60sec native timeout, limited permissions and no Standby. Inspect actual input/output schemas; files follow the [Actor definition](https://docs.apify.com/actors/development/actor-definition/actor-json) and [output schema](https://docs.apify.com/actors/development/actor-definition/output-schema) contracts.
3. Apply `.actor/ppe-deployment.json` in Console monetization: `citation_checked` $0.01 as primary event, native start retained, default dataset event removed. This JSON is a publisher contract and does not auto-activate pricing. Verify actual Store/run `pricingInfo`, limits and event counts including start.
4. Run authorized one-row, empty/unknown, partial-budget, just-short/start-plus-one, duplicate and abort samples in cloud; reconcile output, charged events and actual usage. Local mock/SDK tests do not substitute. Budget private smoke explicitly and keep build/run spend inside the initial$10 cap.
5. Before public activation, the publisher must have native cumulative product spend tracking and an actual disable/unpublish path at creator loss$5 within initial$10. Record refund/platform obligations on stopping. Per-run options and account-wide limits do not establish that product-level cumulative control. No monitoring automation or future continuation is implied by these files.
6. Publish only after the authorized final gate. Verify real listing, sample, prices, private/cloud results and API/MCP retrieval. Public upload/build alone is not publication or external paid demand. The14-day experiment and monthly owner cap remain the adopted management contract; no outreach or ads are authorized here.

Native API uses run creation/completion plus dataset items or KVS `OUTPUT`. Native Apify MCP `call-actor` returns run/storage references; confirm completion and fetch dataset items separately. Memory, timeout and spending limits belong in call options. No custom MCP server is needed. Authenticated API/MCP access and accountless agentic payment eligibility are separate; neither cloud path was exercised by this implementation. [Official MCP documentation](https://docs.apify.com/mcp).

# Actor input Schema

## `authorizedToFetch` (type: `boolean`):

Confirm source permission and terms. The prefilled permission applies to the fixed MDN documentation demo below (license and attribution in README); confirm permission for your own sources. Robots allowance alone does not grant rights.

## `allowedOrigins` (type: `array`):

Exact http(s) origins, e.g. https://example.org. Redirects outside this list are unknown.

## `rows` (type: `array`):

rowId (1–128 characters), public URL and nonempty quote (max300 Unicode code points). Duplicate rowIds are preserved using rowIndex.

## Actor input object example

```json
{
  "authorizedToFetch": true,
  "allowedOrigins": [
    "https://developer.mozilla.org"
  ],
  "rows": [
    {
      "rowId": "mdn-main-demo",
      "url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main",
      "quote": "The main content area consists of content"
    }
  ]
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `output` (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 = {
    "authorizedToFetch": true,
    "allowedOrigins": [
        "https://developer.mozilla.org"
    ],
    "rows": [
        {
            "rowId": "mdn-main-demo",
            "url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main",
            "quote": "The main content area consists of content"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("remarkable_whipscorpion/citation-passage-check").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 = {
    "authorizedToFetch": True,
    "allowedOrigins": ["https://developer.mozilla.org"],
    "rows": [{
            "rowId": "mdn-main-demo",
            "url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main",
            "quote": "The main content area consists of content",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("remarkable_whipscorpion/citation-passage-check").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 '{
  "authorizedToFetch": true,
  "allowedOrigins": [
    "https://developer.mozilla.org"
  ],
  "rows": [
    {
      "rowId": "mdn-main-demo",
      "url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/main",
      "quote": "The main content area consists of content"
    }
  ]
}' |
apify call remarkable_whipscorpion/citation-passage-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,remarkable_whipscorpion/citation-passage-check"
        }
    }
}
```

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/AKgqQ3TYXtbPBDkqL/builds/tKRvLPwDK6uE6560N/openapi.json
