# FAQ Question Answer Extractor (`junipr/faq-question-answer-extractor`) Actor

Extract FAQ question-answer pairs from visible HTML and FAQPage schema, reconcile duplicates, and flag schema/visible mismatches

- **URL**: https://apify.com/junipr/faq-question-answer-extractor.md
- **Developed by:** [junipr](https://apify.com/junipr) (community)
- **Categories:** SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.50 / 1,000 faq source page scanneds

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?

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

## FAQ Question Answer Extractor

Extract question-and-answer pairs from visible page content and `FAQPage` JSON-LD, reconcile matching questions, classify question intent, and identify empty or inconsistent answers.

The Actor is useful for FAQ inventories, structured-data audits, content research, support-content migrations, and duplicate-question cleanup. Every answer comes from accepted page evidence. Missing answers stay null; they are never completed from general knowledge.

### Supported evidence

Visible extraction recognizes:

- `<details>` and `<summary>` pairs;
- FAQ item blocks with question and answer classes;
- definition lists using `<dt>` and `<dd>`;
- question headings followed by a paragraph.

Structured extraction recognizes valid JSON-LD objects with `@type: FAQPage` and `mainEntity` questions containing accepted answers. When a normalized question exists in both sources, the Actor compares the cleaned answer text and records whether it matches exactly.

The Actor also classifies common intent patterns such as cost, process, reason, location, timing, eligibility/confirmation, and general definition questions.

### Input

Supply public FAQ pages in `pageUrls`, additional public pages in `startUrls`, bounded XML sitemaps in `sitemapUrls`, or captured records in `htmlInputs`.

| Field | Purpose | Default / cap |
| --- | --- | --- |
| `pageUrls` | Public pages containing FAQs | empty |
| `startUrls` | Additional public FAQ pages | empty |
| `sitemapUrls` | Public XML sitemaps | empty |
| `htmlInputs` | Captured records with `sourceUrl` and HTML | empty |
| `includeFaqSchema` | Extract `FAQPage` JSON-LD | `true` |
| `compareVisibleAndSchema` | Compare answers for matching questions | `true` |
| `dedupeQuestions` | Collapse repeated questions across pages | `true` |
| `questionFilters` | Optional case-insensitive phrases a question must contain | empty array |
| `maxFaqPairsPerPage` | Maximum retained question pairs per page | 50, maximum 500 |
| `maxPages` | Maximum fetched pages | 1 by default, maximum 25 |
| `allowedDomains` | Optional hostname allowlist | empty |
| `includeEvidence` | Keep evidence excerpts in rows | `true` |
| `includeRawSnapshots` | Store accepted raw HTML | `false` |
| `maxChargeUsd` | Stop before the next event exceeds this amount | 15 USD maximum |

Example:

```json
{
  "pageUrls": [
    "https://product.example/pricing-faq",
    "https://product.example/account-faq"
  ],
  "includeFaqSchema": true,
  "compareVisibleAndSchema": true,
  "dedupeQuestions": true,
  "questionFilters": ["billing", "cancel", "trial"],
  "maxFaqPairsPerPage": 100,
  "maxPages": 2,
  "allowedDomains": ["product.example"],
  "maxChargeUsd": 3
}
```

Question filters are applied after normalization. A question is retained when it contains at least one configured phrase. The Store default has no page source and returns a nonempty `source_required` diagnostic.

### Dataset output

Each dataset item includes:

- `questionText` and `answerText` from the accepted page;
- `answerLength`, with zero used for an observed question whose answer is empty;
- `faqSource`, such as `visible_details`, `visible_definition_list`, `faq_schema`, or `visible_and_schema`;
- `schemaVisibleMatch`, which is true or false when comparison is enabled and null when it is not applicable;
- `duplicateQuestionGroup`, a stable bounded identifier for repeated normalized questions;
- `questionIntent`, such as `cost`, `process`, or `eligibility_or_confirmation`;
- `issueCode`, `severity`, `evidenceSnippet`, and `recommendation` for normal, mismatch, empty-answer, blocked, and missing-source rows;
- `sourceUrl`, `canonicalUrl`, `httpStatus`, and `scannedAt` for provenance.

When the same question appears visibly and in structured data, the visible answer is retained as the primary dataset answer while the match flag reports the comparison. An empty visible answer is preserved as null and marked high severity. A nonempty disagreement is marked medium severity.

With cross-page deduplication enabled, the first row is kept and receives a duplicate group identifier when the question appears again. Disable `dedupeQuestions` if every occurrence must remain in the dataset.

### Reports

With `includeReport: true`, the Actor stores:

- a Markdown summary;
- a complete CSV export;
- JSON issue rows;
- bounded evidence excerpts;
- run metadata with source counts, issue codes, pricing mode, and consumed fields.

Raw accepted HTML is written only when `includeRawSnapshots` is explicitly enabled.

### Pay-per-event pricing

| Event | Price (USD) | When charged |
| --- | ---: | --- |
| `actor-start` | 0.0100 | after successful startup |
| `faq-source-page-scanned` | 0.0065 | once for each accepted FAQ source unit |
| `faq-pair-extracted` | 0.00295 | before each paid question-answer row |
| `faq-mismatch-emitted` | 0.00295 | before each paid mismatch, empty-answer, or diagnostic row |
| `audit-report-generated` | 0.0800 | before report artifacts are stored |

Apify platform usage pass-through is off. The Actor checks `maxChargeUsd` and the complete charge result before every output. It exits before writing a row or report when the charge cannot be accepted.

### Safety and interpretation

Only public HTTP and HTTPS URLs are fetched. Loopback and private IPv4 targets are rejected, an optional hostname allowlist can narrow access, and page counts and request timeouts are bounded. Discovered links are not crawled. The Actor does not bypass authentication or human-verification controls.

Structured-data equality is a cleaned-text comparison, not a semantic fact check. Two differently worded answers may be equivalent, while two identical answers may still be inaccurate. Validate policy, legal, medical, financial, or account-support content with the appropriate owner before publishing changes.

# Actor input Schema

## `pageUrls` (type: `array`):

Public URLs containing visible FAQs or FAQPage structured data.

## `startUrls` (type: `array`):

Optional public page URLs; pageUrls is preferred for role clarity.

## `sitemapUrls` (type: `array`):

Optional public XML sitemap URLs. Accepted pages remain bounded by maxPages.

## `htmlInputs` (type: `array`):

Optional records with sourceUrl and html or currentHtml for deterministic extraction.

## `includeFaqSchema` (type: `boolean`):

Extract FAQPage structured data when present.

## `compareVisibleAndSchema` (type: `boolean`):

Compare visible FAQ text against FAQPage structured data.

## `dedupeQuestions` (type: `boolean`):

Deduplicate repeated questions across scanned pages.

## `questionFilters` (type: `array`):

Optional case-insensitive phrases. Only questions containing at least one phrase are retained.

## `maxFaqPairsPerPage` (type: `integer`):

Maximum FAQ question-answer pairs to emit per page.

## `allowedDomains` (type: `array`):

Optional hostname allowlist for fetched pages.

## `maxPages` (type: `integer`):

Maximum pages to fetch in one run.

## `maxDepth` (type: `integer`):

General link discovery is disabled.

## `includeEvidence` (type: `boolean`):

Include short source evidence snippets in output rows.

## `includeRawSnapshots` (type: `boolean`):

Opt in to raw HTML artifacts in key-value storage.

## `requestDelayMs` (type: `integer`):

Delay in milliseconds between outbound page requests.

## `timeoutMs` (type: `integer`):

Maximum time in milliseconds to wait for a page request.

## `userAgentMode` (type: `string`):

User agent profile to use for public page requests.

## `maxChargeUsd` (type: `number`):

Maximum estimated PPE spend before the actor exits gracefully.

## `includeReport` (type: `boolean`):

Write the markdown summary report to the run key-value store.

## Actor input object example

```json
{
  "pageUrls": [],
  "startUrls": [],
  "sitemapUrls": [],
  "htmlInputs": [],
  "includeFaqSchema": true,
  "compareVisibleAndSchema": true,
  "dedupeQuestions": true,
  "questionFilters": [],
  "maxFaqPairsPerPage": 50,
  "allowedDomains": [],
  "maxPages": 1,
  "maxDepth": 0,
  "includeEvidence": true,
  "includeRawSnapshots": false,
  "requestDelayMs": 250,
  "timeoutMs": 15000,
  "userAgentMode": "standard",
  "maxChargeUsd": 15,
  "includeReport": true
}
```

# Actor output Schema

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

No description

## `report` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("junipr/faq-question-answer-extractor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("junipr/faq-question-answer-extractor").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 '{}' |
apify call junipr/faq-question-answer-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,junipr/faq-question-answer-extractor"
        }
    }
}

```

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/8gqe1qN3zd1QLOkVC/builds/QLXBFG8LZm3pj0cT3/openapi.json
