PII Redaction & Secret Scanner for LLM Pipelines avatar

PII Redaction & Secret Scanner for LLM Pipelines

Pricing

from $0.99 / 1,000 document processeds

Go to Apify Store
PII Redaction & Secret Scanner for LLM Pipelines

PII Redaction & Secret Scanner for LLM Pipelines

Detect and redact PII (emails, phones, SSNs, Luhn-checked cards, IBANs, IPs, optional names and addresses) and secrets (API keys, tokens, private keys) in text, HTML or PDF before it reaches an LLM. Placeholder, mask or hash redaction with a reversible map. $1.50 per 1,000 docs.

Pricing

from $0.99 / 1,000 document processeds

Rating

0.0

(0)

Developer

Fairquery

Fairquery

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

11 days ago

Last modified

Security

Limited permissions

Share

What does it do?

This PII redaction Actor finds and redacts personal data and secrets in text, HTML and PDF documents before they are sent to an LLM, a vector store or a third-party API. It detects emails, phones, SSNs, credit cards (Luhn-checked), IBANs, UK NINOs and IP addresses, optional names and street addresses, and 18 secret types such as AWS, GitHub, Stripe, OpenAI and Anthropic keys, private keys and JWTs.

Each document gets a risk level, a list of findings with offsets and masked previews, and in redact mode a clean output_text. Placeholders are reversible, so you can put the original values back into the model's answer. The Actor never logs document content and, by default, deletes the run's input when it finishes.

How much does it cost?

Pay per event, no subscription. Prices per 1,000 events:

EventFree planStarter (Bronze)Scale (Silver)Business (Gold)
document-processed (primary)$1.50$1.35$1.20$0.99
finding$0.20$0.20$0.20$0.20

The Apify Free plan includes $5 of usage every month, no credit card needed, and this Actor's event charges come out of that $5. That covers about 3,300 clean documents a month ($5 ÷ ($1.50/1,000)), or about 2,600 documents with 2 findings each, plus a start fee of $0.05 per 1,000 runs. Error rows are never charged.

Example (Free-plan prices): 1,000 support tickets with 2,000 findings in total: 1,000 × ($1.50/1,000) + 2,000 × ($0.20/1,000) = $1.90.

Paid Apify plans get the Store discount automatically: Starter = Bronze, Scale = Silver, Business = Gold. Set a maximum cost per run in the run options and the Actor stops cleanly at that limit.

How to use it

  1. Paste Texts, add Documents objects, or give Document URLs (HTML or PDF).
  2. Pick a mode (detect or redact) and a redaction style (placeholder, mask or hash). Turn on names and addresses if you need them.
  3. Click Start. Send output_text to your model, and keep the placeholder map if you want to restore values afterwards.

Example input

{
"texts": [
"Contact jane.doe@example.com or call +1 415-555-0132."
],
"mode": "redact",
"redactionStyle": "placeholder"
}

The Actor deletes each run's input when it finishes, so the input above is placeholder text of the same shape as the text that produced this row.

Example output (one row from a real run on 2026-10-18, shortened to the main fields):

{
"type": "document",
"id": "text-1",
"mode": "redact",
"chars": 53,
"finding_count": 2,
"counts_by_type": {
"EMAIL": 1,
"PHONE": 1
},
"risk_level": "medium",
"findings": [
{
"type": "EMAIL",
"category": "pii",
"rule": "email",
"confidence": "high",
"start": 8,
"end": 28,
"length": 20,
"preview": "j***@e***.com",
"replacement": "[EMAIL_1]"
},
{
"type": "PHONE",
"category": "pii",
"rule": "libphonenumber",
"confidence": "high",
"start": 37,
"end": 52,
"length": 15,
"preview": "+* ***-***-0132",
"replacement": "[PHONE_1]"
}
],
"output_text": "Contact [EMAIL_1] or call [PHONE_1]."
}

FAQ

How do I remove PII from text before sending it to ChatGPT or another LLM?

Run the text through this Actor in redact mode and send output_text to the model. Emails, phones, card numbers, keys and other matches are replaced with placeholders like [EMAIL_1].

Can I restore the original values after the LLM answers?

Yes. Turn on returnPlaceholderMap, then run the model's answer back through the Actor in restore mode with that map.

Does it detect API keys and secrets as well as PII?

Yes: 18 named secret types (for example AWS, GitHub, Slack, Stripe, OpenAI and Anthropic keys, private keys, JWTs) plus generic high-entropy secrets.

Is my data stored?

The Actor is designed not to log document content, writes only result rows and a counts-only summary, and by default tries to delete the run's input record when it finishes (best effort: skipped if the run crashes, times out or is aborted). Results hold the redacted text, masked previews and any document URLs. Original values are stored if you turn on includeFindingValues or returnPlaceholderMap, restore mode outputs the original values, and anything redaction misses stays in the text.

Limits

Pattern-based detection finds most but not all sensitive data: unusual formats, non-English names and scanned PDFs (no OCR) can slip through. Use it as one layer of data protection, not as a compliance certification.

When to use this Actor (LLM / tool selection)

Use it when an agent or pipeline needs to:

  • Strip emails, phone numbers, SSNs, card numbers, IBANs and IP addresses from prompts, chat logs, support tickets or CRM notes before LLM calls
  • Catch leaked credentials (AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, Google, private keys, JWTs, password=...) in logs, code snippets and config files
  • Pseudonymise a dataset for RAG / fine-tuning with consistent placeholders ([EMAIL_1], [PERSON_2])
  • Restore real values in the LLM's answer (mode: "restore")
  • Audit a batch of documents for sensitive-data risk (mode: "detect")

Covers OWASP LLM Top 10 risk LLM02 Sensitive Information Disclosure at the input side. It is a pattern-and-validation engine, not a guarantee: always keep defense in depth.

What it detects

CategoryTypesValidation
PIIEMAIL, PHONE, SSN, CREDIT_CARD, IBAN, UK_NINO, IP_ADDRESSLuhn + issuer prefix for cards, mod-97 + country length for IBANs, libphonenumber for phones, SSN area/group rules
PII (opt-in)PERSON, LOCATION, STREET_ADDRESSOpen-source NER (compromise, English) and street-address patterns
SecretsPRIVATE_KEY, AWS_ACCESS_KEY, AWS_SECRET_KEY, GITHUB_TOKEN, GITLAB_TOKEN, SLACK_TOKEN, SLACK_WEBHOOK, STRIPE_KEY, GOOGLE_API_KEY, OPENAI_KEY, ANTHROPIC_KEY, HUGGINGFACE_TOKEN, NPM_TOKEN, SENDGRID_KEY, TWILIO_KEY, AZURE_STORAGE_KEY, JWT, URL_CREDENTIALSGitleaks-style rules
SecretsGENERIC_SECRET (api_key=…, token: …, password=…)Shannon-entropy threshold (minEntropy), placeholder words ignored
Secrets (opt-in)HIGH_ENTROPY_STRINGAny 20+ char random-looking token

Overlapping matches are resolved (secrets > cards/IBAN > SSN > email > IP > phone > address > names), so a card number is never also reported as a phone.

Redaction styles

redactionStyleExampleReversible
placeholder (default)Mail [EMAIL_1] or call [PHONE_1]Yes, with returnPlaceholderMap and mode: "restore"
maskj***@a***.com, **** **** **** 1111, AKIA****************No
hash[EMAIL:3f2a9c0d1b7e] (salted SHA-256 prefix)No; stable across runs if you set hashSalt

Input

FieldTypeDefaultDescription
textsstring[]– (sample texts are prefilled in the Console form)One document per entry
documents{id,text}[][]Documents with your own IDs
documentUrlsstring[][]Public txt/md/json/csv/HTML/PDF URLs (max 10 MB each)
modedetect | redact | restoreredact
redactionStyleplaceholder | mask | hashplaceholder
returnPlaceholderMapboolfalseAdds placeholder_map per row (contains originals!)
consistentPlaceholdersbooltrueSame value → same placeholder across documents
placeholderMapobject{}For restore mode
hashSaltsecret stringrandom per runFor hash style
detectPii / detectSecretsbooltrue
detectNames / detectAddresses / entropyScanboolfalseOpt-in detectors
minEntropynumber3.5Bits/char for generic secrets
defaultPhoneRegionstringUSFor numbers without + prefix
entityTypesstring[]allOnly report these types
allowListstring[][]Values never flagged
includeFindingValuesboolfalsePut raw values into findings
maxDocuments / maxCharsPerDocumentint1000 / 1,000,000Safety caps
purgeInputAfterRunbooltrueDelete the INPUT record at the end
{
"texts": ["Hi, I'm Jane (jane.doe@acme.com, +1 415-555-0132). Card 4111 1111 1111 1111.", "AWS key AKIAIOSFODNN7EXAMPLE"],
"mode": "redact",
"redactionStyle": "placeholder",
"returnPlaceholderMap": true
}

Output

One row per document (type: "document") plus machine-readable type: "error" rows.

{
"schema_version": "1.0.0", "type": "document", "id": "text-1", "source": "text", "chars": 78, "mode": "redact",
"finding_count": 3, "risk_level": "high",
"counts_by_type": { "EMAIL": 1, "PHONE": 1, "CREDIT_CARD": 1 },
"findings": [
{ "type": "EMAIL", "category": "pii", "rule": "email", "confidence": "high", "start": 14, "end": 31, "length": 17, "preview": "j***@a***.com", "replacement": "[EMAIL_1]" }
],
"output_text": "Hi, I'm Jane ([EMAIL_1], [PHONE_1]). Card [CREDIT_CARD_1].",
"placeholder_map": { "[EMAIL_1]": "jane.doe@acme.com", "[PHONE_1]": "+1 415-555-0132", "[CREDIT_CARD_1]": "4111 1111 1111 1111" }
}

risk_level: high = any secret or card/SSN/IBAN/NI number; medium = other confident PII; low = only low-confidence (NER/entropy) hits; none.

Restore after the LLM answers:

{ "mode": "restore", "texts": ["Dear [EMAIL_1], your card ending [CREDIT_CARD_1]…"], "placeholderMap": { "[EMAIL_1]": "jane.doe@acme.com" } }

Error codes: INPUT_EMPTY, MAX_DOCUMENTS_EXCEEDED, PLACEHOLDER_MAP_MISSING, DOCUMENT_FETCH_FAILED, CHARGE_LIMIT_REACHED / RUN_ABORTED (error rows are never charged). A run that scanned nothing ends as FAILED (exit code 1): no input (INPUT_EMPTY), restore mode without a placeholderMap, or every document failing to load. So an error-only result can never be read as "no PII found".

RUN_SUMMARY (key-value store) holds counts per type, charged events and settings only, never text.

Pricing

EventPriceWhen
document-processed$1.50 per 1,000Each document scanned (or restored)
finding$0.20 per 1,000Each PII or secret finding reported
apify-actor-start$0.05 per 1,000 runsOnce per run, per GB of memory (Apify default event)

Error rows (INPUT_EMPTY, MAX_DOCUMENTS_EXCEEDED, PLACEHOLDER_MAP_MISSING, DOCUMENT_FETCH_FAILED) are never charged.

Worked example: 1,000 support tickets, 2,000 findings in total: 1,000 × ($1.50/1,000) + 2,000 × ($0.20/1,000) = $1.50 + $0.40 = $1.90, plus $0.05 per 1,000 runs. A clean document with no findings costs $1.50 per 1,000.

To cap spend, set a maximum cost per run in the run options; the Actor stops cleanly at the limit. entityTypes limits which types are reported (and charged as findings); maxDocuments caps documents per run. Prices are from the Store pricing tab (October 2026).

Memory / runtime

Runs at a fixed 512 MB (default and maximum; enough for NER), no browser, no proxy. Regex detectors process roughly 1–5 MB of text per second; NER (detectNames) is slower, so split very big batches into several runs. PDFs are parsed with unpdf (text layer only, no OCR).

MCP / Apify tools

Call from agents via Apify MCP with Actor fairquery/llm-data-guard-pii-secrets:

{ "actor": "fairquery/llm-data-guard-pii-secrets", "input": { "texts": ["..."], "mode": "redact" } }

Works well after

  • Website Content Crawler: scrub crawled pages before you index them in a vector store or send them to an LLM.
  • Exports of support tickets, chat logs or CRM notes: run them through mode: "redact" first, call your model, then put the real values back with mode: "restore".

Troubleshooting

SymptomCause and fix
Run FAILED with only an INPUT_EMPTY rowNo texts, documents or documentUrls. The sample texts in the Console form are prefill only and are not sent with API calls.
DOCUMENT_FETCH_FAILEDThe URL was unreachable, over 10 MB or not a supported type. These rows are free. Scanned PDFs have no text layer, and there is no OCR.
Run FAILED with PLACEHOLDER_MAP_MISSING in restore modePass the placeholderMap returned by the redact run (returnPlaceholderMap: true). Nothing was processed or charged.
Too many findings (false positives)Add known-safe values to allowList, raise minEntropy, keep entropyScan off, or limit entityTypes.
Names or addresses not detecteddetectNames and detectAddresses are opt-in and English-focused. Turn them on (they are slower; split very big batches).

FAQ

Is document content stored?

The Actor is designed not to log document content. Results go only to your run's dataset, RUN_SUMMARY holds counts only, and by default (purgeInputAfterRun) the Actor tries to delete the INPUT record at the end; this is best effort and is skipped if the run crashes, times out or is aborted. Delete the dataset when you no longer need it.

Can I get the original values back after the LLM answers?

Yes. Use redactionStyle: "placeholder" with returnPlaceholderMap: true, then run mode: "restore" with that map.

Does it catch everything?

No. It is a pattern-and-validation engine (checksums, issuer prefixes, Gitleaks-style rules). Unusual formats, non-English names and images can slip through. Use it as one layer of defence.

How much does it cost?

$1.50 per 1,000 documents plus $0.20 per 1,000 findings, and a start fee of $0.05 per 1,000 runs. Error rows are free.

Can I scan text without sending it to Apify?

Yes, through Apify MCP with Actor fairquery/llm-data-guard-pii-secrets.

Fairquery family

Other Fairquery tools on the Apify Store. Each one returns structured rows with machine-readable error codes, and error rows are never charged:

All Fairquery Actors: https://apify.com/fairquery

Data handling, security and support

  • Data retention: Text is processed in memory, and the Actor is designed not to log it. By default the INPUT record is deleted when the run ends (purgeInputAfterRun), so only the results dataset remains in your Apify account, under your Apify plan's retention. You can delete run storages and named stores at any time in Apify Console or through the API. Fairquery's code sends nothing to Fairquery.
  • Security reports: email info@getfairquery.com.
  • Support: the Issues tab on this page or info@getfairquery.com. We aim to reply within 3 business days (a target, not an SLA).

Disclaimer

Pattern-based detection finds most but not all sensitive data: unusual formats, non-English names, images and scanned PDFs (no OCR) can slip through, and some matches are false positives. Use it as one layer of a data-protection program, not as a compliance certification. Only process data you are allowed to process.