# Content Style & Brand Voice Checker (`datascraperes/website-content-style-brand-terminology-checker`) Actor

Audit website copy, documentation, Markdown, HTML, and direct text with Vale. Detect editorial style and terminology issues, enforce brand language, and export structured reports in bulk.

- **URL**: https://apify.com/datascraperes/website-content-style-brand-terminology-checker.md
- **Developed by:** [DataScraperES](https://apify.com/datascraperes) (community)
- **Categories:** Marketing, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.01 / 1,000 analyzed words

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

Content Style & Brand Voice Checker audits website copy, documentation, blog posts, Markdown, HTML, and direct text against deterministic editorial rules and your own terminology policy. Use it to enforce consistent writing style and brand language in bulk, with one structured Dataset item per document containing severity counts, a 0–100 score, and every finding produced by Vale.

### What this Actor does

- Fetches multiple public HTML or plain-text URLs and extracts their readable content.
- Analyzes direct text, Markdown, HTML, reStructuredText, AsciiDoc, and DocBook XML.
- Offers `technical`, `marketing`, and `minimal` rule presets.
- Enforces required terms, forbidden terms, preferred capitalization, replacements, and forbidden phrases.
- Continues after individual URL failures and records a failed item for each affected document.
- Stores per-document findings in the Dataset and aggregate totals in the `SUMMARY` key-value-store record.

This is a deterministic style and terminology checker. It does not infer semantic tone, personality, factual accuracy, or whether content “sounds like” a brand.

### Use cases

- Audit product documentation before publication.
- Check terminology consistency across a website migration.
- Find prohibited or outdated language on many pages at once.
- Enforce product-name capitalization in marketing and support content.
- Export review evidence to automated content operations or reporting tools.

### How to use

1. Add one or more public URLs, direct text documents, or both.
2. Choose the style preset that best matches the content.
3. Add any required, forbidden, replacement, or capitalization rules.
4. Run the Actor.
5. Open the Dataset for document-level findings or the `SUMMARY` record for totals.

The following input was used for the representative output below:

```json
{
  "style": "technical",
  "texts": [
    {
      "id": "example-guide",
      "format": "markdown",
      "text": "# Quick Start.\n\nWe will simply utilize the apify actor. The blacklist is updated by the system."
    }
  ],
  "requiredTerms": ["Dataset"],
  "preferredCaseTerms": ["Apify", "Actor", "Dataset", "API"],
  "forbiddenTerms": [
    { "term": "blacklist", "replacement": "blocklist" }
  ],
  "forbiddenPhrases": ["simply"],
  "urls": [],
  "fetchTimeoutSeconds": 10,
  "maxDownloadMb": 1,
  "analysisBatchSize": 10
}
```

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `urls` | string array | `[]` | One public HTTP(S) HTML or plain-text URL per line. Exact duplicates are skipped; JavaScript is not executed. |
| `texts` | object array | `[]` | JSON documents with required `text`, optional `id`, and optional `format`. Supported formats: `text`, `markdown`, `html`, `rst`, `asciidoc`, and `xml`; each document is limited to 2,000,000 characters. |
| `style` | string | `technical` | `technical` uses Google documentation rules, `marketing` uses write-good readability rules, and `minimal` applies only sentence-length and terminology checks. |
| `requiredTerms` | string array | `[]` | Case-insensitive literal terms or phrases that must appear in every document. Each missing entry produces a document-level error. |
| `preferredCaseTerms` | string array | `Apify`, `Actor`, `Dataset`, `API` | Canonical spellings used to report lowercase forms such as `apify` and recommend `Apify`. The defaults can be edited or cleared. |
| `forbiddenTerms` | object array | `[]` | JSON objects with `term` and an optional `replacement`, such as `{"term":"blacklist","replacement":"blocklist"}`. Values are literal, not regular expressions. |
| `forbiddenPhrases` | string array | `[]` | Case-insensitive literal phrases to report. Regular-expression metacharacters have no special meaning. |
| `fetchTimeoutSeconds` | integer | `10` | Per-request timeout from 1 to 60 seconds. A timed-out URL becomes a free failed row without stopping other documents. |
| `maxDownloadMb` | integer | `1` | Maximum response body per URL, from 1 to 9 MiB. Pages exceeding it become free failed rows. |
| `analysisBatchSize` | integer | `10` | Fetched URLs retained per Vale pass, from 1 to 100. Lower values use less peak memory; higher values can help many small pages. |

At least one URL or text document is required. A run accepts up to 500 documents in total.

Terminology lists accept up to 500 non-empty entries of at most 200 characters. `requiredTerms` uses case-insensitive literal substring matching. `forbiddenTerms` must be entered as a JSON array, while the other terminology fields accept one value per line in the Console form.

### Output

Every accepted document produces one Dataset item. This is the complete Dataset item from a real successful run using the exact input above:

```json
{
  "id": "example-guide",
  "sourceType": "text",
  "sourceUrl": null,
  "finalUrl": null,
  "title": null,
  "extraction": "provided",
  "status": "succeeded",
  "words": 16,
  "issues": 8,
  "counts": { "error": 1, "warning": 6, "suggestion": 1 },
  "score": 36,
  "lineReference": "original_input",
  "results": [
    {
      "line": 1,
      "span": [3, 14],
      "severity": "warning",
      "rule": "Google.Headings",
      "message": "'Quick Start.' should use sentence-style capitalization.",
      "match": "Quick Start.",
      "action": null
    },
    {
      "line": 1,
      "span": [13, 14],
      "severity": "warning",
      "rule": "Google.HeadingPunctuation",
      "message": "Don't put a period at the end of a heading.",
      "match": "t.",
      "action": { "name": "edit", "params": ["trim_right", "."] }
    },
    {
      "line": 3,
      "span": [9, 14],
      "severity": "warning",
      "rule": "Customer.Forbidden",
      "message": "Avoid the custom term 'simply'.",
      "match": "simply",
      "action": null
    },
    {
      "line": 3,
      "span": [28, 32],
      "severity": "warning",
      "rule": "Customer.PreferredTerms",
      "message": "Use 'Apify' instead of 'apify'.",
      "match": "apify",
      "action": null
    },
    {
      "line": 3,
      "span": [34, 38],
      "severity": "warning",
      "rule": "Customer.PreferredTerms",
      "message": "Use 'Actor' instead of 'actor'.",
      "match": "actor",
      "action": null
    },
    {
      "line": 3,
      "span": [45, 53],
      "severity": "warning",
      "rule": "Customer.PreferredTerms",
      "message": "Use 'blocklist' instead of 'blacklist'.",
      "match": "blacklist",
      "action": null
    },
    {
      "line": 3,
      "span": [55, 64],
      "severity": "suggestion",
      "rule": "Google.Passive",
      "message": "In general, use active voice instead of passive voice ('is updated').",
      "match": "is updated",
      "action": null
    },
    {
      "line": null,
      "span": null,
      "severity": "error",
      "rule": "Product.RequiredTerm",
      "message": "Required term 'Dataset' is missing.",
      "match": null,
      "action": null
    }
  ],
  "error": null,
  "style": "technical",
  "engine": { "name": "Vale", "version": "3.18.0", "style": "technical" },
  "chargedWords": 16
}
```

For direct content, `line` and `span` refer to the original supplied document. For URLs, they refer to the extracted readable content, not the page source or CMS editor. `chargedWords` shows the exact number of words billed for that row. Failed documents have `status: "failed"`, an `error` message, zero counts, an empty `results` array, and `chargedWords: 0`.

The `SUMMARY` record contains the overall status, document and word totals, severity counts, issue totals grouped by rule, duplicate URL count, engine details, analysis duration, and a billing breakdown with eligible words, charged words, estimated charge, and charge-limit status.

### Pricing

The Actor uses flat pay-per-event billing based only on words in successfully analyzed documents.

| Successfully analyzed content | Price |
| --- | ---: |
| 1 word | `$0.00001` |
| 1,000 words | `$0.01` |
| 100,000 words | `$1.00` |

- There is no Actor-start charge.
- There are no pricing tiers.
- Apify platform usage is included and is not billed separately to the customer.
- Failed, empty, unsupported, and charge-limit-skipped documents have `chargedWords: 0`.
- Before processing a paid document, the Actor checks that the remaining run budget can cover every word. If not, it publishes a `skipped` status without findings and without charging any part of that document.

The charged word count is visible in each Dataset row as `chargedWords` and in the `SUMMARY` record. The Actor's **Pricing** tab remains the source of truth for the active Store price.

### API and integrations

You can run the Actor from Apify Console, the Apify API, schedules, webhooks, or integrations. Dataset items are available through the standard Dataset API, while aggregate totals are available from the default key-value store under the `SUMMARY` key.

Typical automation flow:

1. Send the same JSON input shown in Console to the Actor run endpoint.
2. Wait for the run to finish.
3. Read document rows from the default Dataset.
4. Read aggregate totals from the `SUMMARY` record.

### Related Actors

| Actor | Best for |
| --- | --- |
| [PDF Text Extractor - OCR, Markdown & JSON](https://apify.com/datascraperes/pdf-text-extractor) | Extract PDF content as text or Markdown before applying a terminology audit. |
| [Bulk Website Screenshot & PDF Generator](https://apify.com/datascraperes/website-screenshot-pdf) | Capture visual evidence of the pages included in a content review. |
| [Website Technology Lookup – CMS, Hosting & Tech Stack Data](https://apify.com/datascraperes/website-technology-lookup) | Add CMS and technology context to the websites you are auditing. |

### Limits and data quality

- The default presets contain English editorial rules. Terminology checks can match other languages, but multilingual grammar and spelling are not claimed.
- URL mode supports server-rendered public HTML and plain text. It does not execute page JavaScript or discover an entire site automatically.
- Readability extraction may omit navigation, footers, cookie banners, or other non-article content.
- URLs resolving to private or unsupported network addresses are rejected. Redirects, response types, request time, and download size are limited.
- The score is a deterministic 0–100 severity-and-word-count heuristic. Use the individual findings for editorial decisions.
- A page failing to fetch does not discard successful results from other inputs.

### Frequently asked questions

**Does this Actor rewrite content?**

No. It reports findings and may include a safe replacement hint supplied by a rule.

**Is this a grammar checker or spell checker?**

It is a writing style and terminology checker, not a general-purpose spelling service. The English presets can identify issues such as passive voice, wordiness, heading style, and sentence length, while custom fields enforce your explicit terminology policy.

**How does the brand voice checker work?**

It converts explicit brand-language requirements into deterministic checks for required terms, prohibited wording, preferred capitalization, and forbidden phrases. It does not infer personality or semantic tone with AI.

**Can I upload arbitrary Vale YAML rules?**

No. Customization is limited to declarative terminology and phrase fields.

**Does it crawl every page on a domain?**

No. Supply each page URL explicitly.

**Why do URL line numbers differ from my page source?**

URL findings refer to the extracted readable document, because navigation and other page chrome are removed before analysis.

**What happens when one URL fails?**

The Dataset receives a free failed item with `chargedWords: 0`, and the Actor continues processing other documents.

**What happens when my maximum charge is too low for the next document?**

That document is not partially billed. Its findings and word count are withheld, and the Dataset receives a free `skipped` item explaining that the charge limit was reached.

### Responsible use

Only submit content and URLs you are authorized to process. Avoid sending secrets, credentials, personal data, unpublished confidential material, or content that should not be stored in Actor run storage. Respect website terms and applicable laws when analyzing public pages.

### Support

If a run behaves unexpectedly, open an issue from the Actor page and include the run ID, the affected input type, and the relevant Dataset item. Do not include credentials or confidential text in the support message.

# Actor input Schema

## `urls` (type: `array`):

Enter one complete public HTTP or HTTPS URL per line. The Actor accepts server-rendered HTML and text/plain pages, follows up to 5 public redirects, extracts readable content, and does not execute page JavaScript. Exact duplicate URLs are trimmed and analyzed once. Every URL produces its own succeeded or failed Dataset row; failed URLs are not charged. URLs and direct text documents combined cannot exceed 500.

## `texts` (type: `array`):

Enter a JSON array of document objects. Each object requires a non-empty text field and can include an id plus format. Example: \[{"id":"homepage","format":"markdown","text":"# Welcome\nYour content here."}]. Supported formats are text, markdown, html, rst, asciidoc, and xml; format defaults to text and id is generated when omitted. Each document can contain up to 2,000,000 characters and counts toward the 500-document run limit. Finding line numbers refer to this original supplied content.

## `style` (type: `string`):

Choose the editorial rule set applied to every document. Technical applies Google documentation-style rules plus sentence-length and terminology checks. Marketing applies write-good readability rules plus sentence-length and terminology checks. Minimal applies only sentence-length and your terminology rules, making it the least opinionated option. This choice changes findings, not URL extraction or billing.

## `requiredTerms` (type: `array`):

Enter one literal term or phrase per line that must appear in every analyzed document. Matching is case-insensitive and checks literal text, including substrings; it is not a regular expression or whole-word rule. Each missing entry adds one document-level error. You can provide up to 500 non-empty entries of at most 200 characters each. Leave empty when no terminology is mandatory.

## `preferredCaseTerms` (type: `array`):

Enter the canonical spelling and capitalization of product names, acronyms, or branded terms, one per line. For example, Apify causes the lowercase form apify to be reported with a recommendation to use Apify, while the exact preferred spelling is accepted. Values are treated as literal text, not regular expressions. Up to 500 non-empty entries of at most 200 characters are accepted. The defaults can be edited or cleared if they do not apply to your content.

## `forbiddenTerms` (type: `array`):

Enter a JSON array of objects containing term and, optionally, replacement. Example: \[{"term":"blacklist","replacement":"blocklist"},{"term":"simply"}]. The term is matched as literal text, never as a regular expression. A replacement makes the warning recommend that wording; without one, the Actor only reports that the term should be avoided. Up to 500 objects are accepted, with terms and replacements limited to 200 characters.

## `forbiddenPhrases` (type: `array`):

Enter one literal word or multi-word phrase per line. Matching is case-insensitive, and characters such as ., \*, +, ?, or brackets are treated literally rather than as regular-expression syntax. Each occurrence produces a warning. Use this for banned slogans, filler phrases, or wording that has no replacement. Up to 500 non-empty entries of at most 200 characters are accepted.

## `fetchTimeoutSeconds` (type: `integer`):

Maximum waiting time for each URL request, from 1 to 60 seconds. The default 10 seconds is suitable for most public pages. Increase it only for consistently slow sites; doing so can make a run take longer when a server is unavailable. A timeout affects only that URL, which receives a free failed Dataset row while other documents continue.

## `maxDownloadMb` (type: `integer`):

Maximum response body downloaded from each URL, from 1 to 9 MiB. The default 1 MiB is enough for most article and documentation pages and limits memory use. Increase it only when valid text pages are being rejected for size. A page exceeding the limit becomes a free failed row; this setting does not limit direct text documents.

## `analysisBatchSize` (type: `integer`):

Number of successfully fetched URL documents retained for each Vale analysis pass, from 1 to 100. Keep the recommended default of 10 for balanced memory usage. Lower values reduce peak memory for unusually large pages but require more analysis passes; higher values may improve throughput for many small pages while using more memory. This setting changes batching only and does not change findings, prices, or the 500-document limit.

## Actor input object example

```json
{
  "urls": [],
  "texts": [],
  "style": "technical",
  "requiredTerms": [],
  "preferredCaseTerms": [
    "Apify",
    "Actor",
    "Dataset",
    "API"
  ],
  "forbiddenTerms": [],
  "forbiddenPhrases": [],
  "fetchTimeoutSeconds": 10,
  "maxDownloadMb": 1,
  "analysisBatchSize": 10
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/website-content-style-brand-terminology-checker").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("datascraperes/website-content-style-brand-terminology-checker").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 datascraperes/website-content-style-brand-terminology-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datascraperes/website-content-style-brand-terminology-checker"
        }
    }
}

```

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/t1I0O4RUMQCsOi1vv/builds/DzVeh1TYj8hT7KbnT/openapi.json
