# Quote Source Verifier (`automation-lab/source-quote-verifier`) Actor

Verify quotations against public source pages and export matched passages, similarity, context, provenance URLs, HTTP status, and checked timestamps.

- **URL**: https://apify.com/automation-lab/source-quote-verifier.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools, Automation, Education
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.47 / 1,000 verification extracteds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Quote Source Verifier

Verify a supplied quotation against a supplied anonymous public page.

Quote Source Verifier is a focused **quote source** checker for editorial,
research, publishing, and content-quality workflows.

It returns one typed finding for every quote and URL pair, including:

- match status;
- deterministic similarity;
- the best matched passage;
- surrounding context;
- provenance and final URLs;
- HTTP status and content type;
- a checked timestamp.

The Actor does not invent a source or use an LLM to judge truth.
It answers the narrower, auditable question:
“Does this public page contain this claimed quotation?”

### What can you do with Quote Source Verifier?

Use it to:

- check quotations before an article is published;
- audit citations in a research spreadsheet;
- recheck source pages on a schedule;
- flag copy edits that changed quoted wording;
- retain source evidence alongside an editorial record;
- distinguish absent text from an unreachable page;
- export verification findings to Sheets, a database, or a QA queue.

Every result preserves the supplied quote and optional reference ID.
That makes it easy to join findings back to a CMS or research table.

### Who is it for?

**Editors** can review exact, partial, and absent quotations before publication.

**Researchers** can preserve the source URL, matched passage, and timestamp.

**Fact-checking teams** can triage wording differences without opaque AI output.

**Content operations teams** can schedule repeat checks through Apify Tasks.

**Developers** can call the Actor through the API and consume typed JSON rows.

### How verification works

1. The Actor validates each public HTTP or HTTPS URL.
2. It fetches each unique page once per run.
3. It extracts readable HTML or plain-text content.
4. It normalizes Unicode quotation marks, case, and whitespace.
5. It first looks for an exact normalized match.
6. If needed, it compares bounded word windows.
7. Fuzzy scoring combines token overlap and ordered bigram overlap.
8. It returns evidence and context rather than only a boolean.

Exact normalized matches score `1`.

Fuzzy findings at or above `minimumSimilarity` are `matched`.

Lower but useful candidates are `partial`.

Weak candidates are `not_found` and do not expose misleading passage text.

### Input

The required `checks` array contains quote and URL pairs.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `checks` | array | required | Quote, public source URL, and optional reference ID. |
| `minimumSimilarity` | number | `0.82` | Threshold from 0.5 to 1 for fuzzy matched status. |
| `contextCharacters` | integer | `240` | Characters retained before and after a useful passage. |
| `maxChecks` | integer | `100` | Safety limit, up to 1,000 checks. |
| `requestTimeoutSecs` | integer | `30` | Per-request timeout from 5 to 120 seconds. |
| `proxyConfiguration` | object | direct | Optional Apify Proxy settings. |

Repeated URLs are downloaded once within a run.
Each quote still receives its own result.

Example input:

```json
{
  "checks": [
    {
      "referenceId": "declaration-equality",
      "url": "https://www.archives.gov/founding-docs/declaration-transcript",
      "quote": "We hold these truths to be self-evident, that all men are created equal"
    }
  ],
  "minimumSimilarity": 0.82,
  "contextCharacters": 240
}
```

### Output fields

| Field | Meaning |
| --- | --- |
| `referenceId` | Optional identifier copied from input. |
| `quote` | Original quotation being checked. |
| `sourceUrl` | Supplied provenance URL. |
| `finalUrl` | URL after redirects, when fetched. |
| `sourceTitle` | Normalized HTML title, when available. |
| `matchStatus` | `matched`, `partial`, `not_found`, or `fetch_failed`. |
| `matchMethod` | `exact_normalized`, `fuzzy`, or `none`. |
| `similarity` | Deterministic score from 0 to 1. |
| `matchedPassage` | Best useful source passage. |
| `contextBefore` | Source text before the passage. |
| `contextAfter` | Source text after the passage. |
| `httpStatus` | Source response status, when available. |
| `contentType` | Source response content type. |
| `error` | Bounded fetch diagnostic. |
| `checkedAt` | ISO 8601 verification timestamp. |

Example result:

```json
{
  "referenceId": "declaration-equality",
  "quote": "We hold these truths to be self-evident, that all men are created equal",
  "sourceUrl": "https://www.archives.gov/founding-docs/declaration-transcript",
  "finalUrl": "https://www.archives.gov/founding-docs/declaration-transcript",
  "sourceTitle": "declaration of independence: a transcription",
  "matchStatus": "matched",
  "matchMethod": "exact_normalized",
  "similarity": 1,
  "matchedPassage": "we hold these truths to be self-evident, that all men are created equal",
  "contextBefore": "we hold these truths to be",
  "contextAfter": "that they are endowed by their creator",
  "httpStatus": 200,
  "contentType": "text/html; charset=utf-8",
  "error": null,
  "checkedAt": "2026-01-15T12:00:00.000Z"
}
```

Actual capitalization in evidence follows the readable source page.
Fields may be null when the server does not provide the evidence.

### Match statuses

#### `matched`

The exact normalized quote was found, or the strongest fuzzy passage met the
configured threshold.

#### `partial`

A meaningful candidate was found, but it did not meet the matched threshold.
An editor should inspect the passage and context.

#### `not_found`

No sufficiently similar passage was present in the readable page text.
The HTTP evidence still shows whether the page was fetched successfully.

#### `fetch_failed`

The page could not be retrieved or parsed after bounded retries.
Failed fetches are returned for diagnosis and are not charged as verifications.

### How much does it cost to verify quote sources?

Pay-per-event pricing keeps small and recurring jobs predictable.

- A run has a **$0.005 start** event.
- A successful source-page quote check is charged as one `verification` event.
- Failed fetch results have no verification event charge.
- On the BRONZE plan, one verification is **$0.00912**.

Example BRONZE totals:

| Successful checks | Estimated total |
| ---: | ---: |
| 1 | $0.01412 |
| 10 | $0.09620 |
| 100 | $0.91700 |

Higher Apify plans receive tier discounts shown before the run starts.
Proxy transfer, if explicitly enabled, is accounted for by Apify usage.
There is no automatic residential-proxy fallback.

### Getting started

1. Open the Actor input page.
2. Add one or more quote and public URL pairs.
3. Keep the default similarity threshold for the first run.
4. Click **Start**.
5. Open the default dataset.
6. Filter by `matchStatus`.
7. Review partial and not-found rows.
8. Export JSON, CSV, Excel, or connect an integration.

Start with exact quotations from server-rendered pages.
Only lower the similarity threshold when your workflow tolerates looser wording.

### Editorial QA workflow

Give each input a stable `referenceId`, such as a CMS article and quote number.

Run the Actor before publication.

Route:

- `matched` rows to automatic acceptance;
- `partial` rows to manual wording review;
- `not_found` rows to source investigation;
- `fetch_failed` rows to retry or access review.

Schedule the same Task to detect changed or removed source text.
Compare datasets by `referenceId`, `matchStatus`, and `similarity`.
The Actor itself does not send alerts or maintain historical state.

### Spreadsheet and data-pipeline integration

The default dataset works with Apify integrations and webhooks.

Useful patterns include:

- send completed rows to Google Sheets;
- trigger a webhook when a scheduled run finishes;
- load JSON rows into a warehouse;
- join results to CMS records by `referenceId`;
- filter non-matches in Make or Zapier;
- archive source evidence with a research package.

No separate export event is charged.

### Run with the API

Replace `YOUR_TOKEN` with an Apify API token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~source-quote-verifier/runs?token=YOUR_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{"checks":[{"url":"https://www.archives.gov/founding-docs/declaration-transcript","quote":"We hold these truths to be self-evident"}]}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/source-quote-verifier').call({
  checks: [{
    url: 'https://www.archives.gov/founding-docs/declaration-transcript',
    quote: 'We hold these truths to be self-evident',
  }],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation-lab/source-quote-verifier").call(run_input={
    "checks": [{
        "url": "https://www.archives.gov/founding-docs/declaration-transcript",
        "quote": "We hold these truths to be self-evident",
    }]
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP and AI agents

Add the Actor to Claude Code through Apify MCP:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/source-quote-verifier"
```

#### Claude Desktop, Cursor, and VS Code setup

Use this HTTP MCP configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/source-quote-verifier"
    }
  }
}
```

Example prompts:

- “Check these quotations against their listed public sources.”
- “Return only partial, not-found, or failed editorial checks.”
- “Verify this public-domain book quote and preserve surrounding context.”

An agent should treat `partial` as review evidence, not proof of identity.

### Reliability and retries

Requests use a browser-like HTTP client and follow redirects.

Transient network failures, HTTP 429, and server 5xx responses are retried up
to three times with bounded backoff.

Stable HTTP errors are returned without blind retries.

Each response is limited to 5 MB.
Each unique URL is cached only for the current run.

Use optional Apify Proxy settings when a public page blocks cloud traffic.
The Actor never silently enables a paid residential proxy.

### Limitations

The Actor supports anonymous HTML, XHTML, and plain-text pages.

It does not currently:

- render JavaScript-only content;
- log into websites;
- solve CAPTCHAs;
- extract PDFs or scanned images;
- transcribe audio or video;
- search the web for an unknown source;
- decide whether the quoted claim is true;
- preserve page snapshots;
- guarantee that a page is authoritative.

Very short or generic quotations may match unrelated text.
Layout text can differ from visible text on complex pages.
Fuzzy similarity is deterministic but remains a triage signal.

### Legality and responsible use

Check only public pages you are allowed to access.

Respect website terms, robots guidance, copyright, rate limits, and applicable
privacy rules.

A matched passage proves textual similarity at the checked URL and time.
It does not prove authorship, truth, originality, or permission to republish.
Keep human review for consequential editorial or legal decisions.

### Troubleshooting

**Why did a visible quote return `not_found`?**

The text may be rendered by JavaScript, embedded in a PDF, split by unusual
markup, or worded differently. Inspect `httpStatus`, `contentType`, and the page.
Try a complete quotation and cautiously lower `minimumSimilarity`.

**Why did I receive `fetch_failed`?**

The server may block cloud traffic, time out, or return unsupported content.
Try an Apify proxy you are authorized to use or choose an anonymous HTML source.

**Why is a result `partial`?**

The strongest passage crossed the review threshold but not your match threshold.
Compare `matchedPassage`, context, and the original quote manually.

**Why are several checks faster than expected?**

Repeated source URLs are fetched once per run and reused for all their quotes.

### FAQ

**Does this Actor find the source of an unknown quote?**

No. You supply the candidate source URL. This prevents opaque web-search claims
and keeps provenance explicit.

**Is matching case-sensitive?**

No. Exact normalized matching ignores case, whitespace differences, and common
curly-versus-straight quotation-mark differences.

**Are absent quotes charged?**

A successfully fetched and completed verification is charged whether matched or
not. A failed fetch has no verification charge.

**Can I process multiple quotes from one page?**

Yes. Repeat the URL in `checks`; the page is downloaded once in that run.

**Can I schedule recurring checks?**

Yes. Save the input as an Apify Task and choose a schedule. Compare resulting
datasets externally; the Actor does not send alerts by itself.

### Related Automation Lab Actors

- [Webpage Text Extractor](https://apify.com/automation-lab/webpage-text-extractor)
  exports complete readable text when you need the page content rather than a
  quote verdict.
- [Website Uptime Checker](https://apify.com/automation-lab/website-uptime-checker)
  monitors availability and response details without text matching.
- [LLM Web Page Research Browser](https://apify.com/automation-lab/llm-web-page-research-browser)
  supports broader cited page research when deterministic supplied-source
  verification is too narrow.

Choose this Actor when the source and quote are already known and auditability
matters more than discovery or generative analysis.

# Actor input Schema

## `checks` (type: `array`):

One result is produced for each quote and URL pair. Repeated URLs are fetched only once per run.

## `minimumSimilarity` (type: `number`):

Fuzzy-match score required for matched status. Exact normalized matches always score 1.

## `contextCharacters` (type: `integer`):

Maximum characters retained before and after a matched passage.

## `maxChecks` (type: `integer`):

Safety limit on quote and source pairs processed in this run.

## `requestTimeoutSecs` (type: `integer`):

Timeout for each public source request.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy settings for public pages that restrict datacenter traffic.

## Actor input object example

```json
{
  "checks": [
    {
      "referenceId": "declaration-equality",
      "url": "https://www.archives.gov/founding-docs/declaration-transcript",
      "quote": "We hold these truths to be self-evident, that all men are created equal"
    }
  ],
  "minimumSimilarity": 0.82,
  "contextCharacters": 240,
  "maxChecks": 100,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Default dataset view containing one result for each supplied quote and source pair.

# 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 = {
    "checks": [
        {
            "referenceId": "declaration-equality",
            "url": "https://www.archives.gov/founding-docs/declaration-transcript",
            "quote": "We hold these truths to be self-evident, that all men are created equal"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/source-quote-verifier").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 = { "checks": [{
            "referenceId": "declaration-equality",
            "url": "https://www.archives.gov/founding-docs/declaration-transcript",
            "quote": "We hold these truths to be self-evident, that all men are created equal",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/source-quote-verifier").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 '{
  "checks": [
    {
      "referenceId": "declaration-equality",
      "url": "https://www.archives.gov/founding-docs/declaration-transcript",
      "quote": "We hold these truths to be self-evident, that all men are created equal"
    }
  ]
}' |
apify call automation-lab/source-quote-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/source-quote-verifier"
        }
    }
}
```

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/rt36ObPoEeYznQMm3/builds/sctlJ3ao9gqtB9XrG/openapi.json
