Detect and redact personal data in one call
A single HTTPS endpoint finds names, emails, phone numbers, payment cards, national identifiers, addresses, IPs and more in free text — then replaces, masks, hashes or reversibly encrypts them. No models to host, no data retained.
https://pii.api.efficientstack.com · JSON in, JSON out · every response carries an
X-Request-Id header — quote it in support requests.Your first request
Try requests interactively — including operator configuration and batching — in the playground.
Authentication
Send your API key as a bearer token on every request. X-API-Key is accepted for clients that
cannot set Authorization.
Authorization: Bearer pg_live_7f3kq9xw2mba_…
Keys are shown once at creation and stored only as an irreversible digest, so they cannot be recovered — rotate
to replace a lost key. Each key carries a set of permissions (detect,
redact, anonymize, deanonymize,
read) and its own limits.
test keys in non-production environments.Billing & usage
Charges are computed per request from three components and a floor:
| Component | Unit price | Applies to |
|---|---|---|
| Per request | $0.000050 | Every accepted API call. |
| Per 1,000 characters | $0.000500 | Total input length across the request. |
| Minimum charge | $0.000050 | Floor applied to the computed amount. |
Failed requests are never charged. Validation errors, permission errors and service faults cost nothing. Only successfully processed calls are metered.
Every billable response reports what it cost in usage.amount, and the running period total is
returned in the X-Usage-Month-Amount header. Call
GET /v1/usage for period-to-date totals, per-task breakdown and remaining
allowances. Billing periods are calendar months in UTC.
texts array costs less than
256 single-document calls — and is dramatically faster.Limits
| Limit | Default | Notes |
|---|---|---|
| Documents per request | 256 | Length of the texts array. |
| Characters per request | 500,000 | Summed across all documents. |
| Request body | 4 MB | Hard ceiling. |
| Allow-list entries | 1,000 | Per request. |
| Operators | 64 | Per request. |
| Monthly allowances | account-specific | Optional caps on requests, documents or spend. Reported in GET /v1/usage. |
Request-shape limits and monthly allowances can both be raised per account. Contact your sales representative at support@efficientstack.com.
There is no fixed requests-per-second cap. Concurrency is shaped adaptively; if you receive a
503 capacity_unavailable, retry with exponential backoff and jitter.
Errors & retries
Errors return a single envelope. type is stable and safe to branch on; code is more
specific and may gain new values over time.
| HTTP | type | When |
|---|---|---|
| 400 | invalid_request | Malformed body, unknown operator type, batch too large, threshold out of range. |
| 401 | authentication_error | Missing, malformed, disabled, revoked or expired API key. |
| 402 | limit_error | A monthly request, document or spend limit on the key has been reached. |
| 403 | permission_error | The key is not permitted to call this endpoint. |
| 404 | not_found | Unknown endpoint, or a job that does not belong to this key. |
| 413 | invalid_request | Body above 4 MB, or input above the character limit for the key. |
| 429 | invalid_request | Reserved. Concurrency is shaped, not hard-limited — retry with backoff if you receive it. |
| 5xx | service_error | Transient service fault. Retry with exponential backoff; quote request_id if it persists. |
Retry 5xx responses; do not retry 4xx without changing the request. A robust policy is
three attempts at 1 s, 3 s and 9 s with ±20% jitter, carrying the same Idempotency-Key.
Idempotency
Send Idempotency-Key: <uuid> on any POST. The first successful response is stored for 24 hours
and replayed verbatim for any retry using the same key, flagged with Idempotent-Replay: true. Retries
are never charged twice.
-H "Idempotency-Key: 8f14e45f-ea0b-4d1c-9c1a-6b2f0e5c7a91"
POST /v1/detect
Locates personal data and returns spans with confidence scores. The document is never modified.
| Parameter | Type | Description |
|---|---|---|
| text | string | A single document to analyze. Mutually exclusive with texts. |
| texts | string[] | A batch of documents processed in one call. |
| language | string = "en" | Analyzer language. See GET /v1/entities. |
| score_threshold | number = 0.35 | Minimum confidence, 0–1. Lower values find more entities with more false positives. |
| entities | string[] | Restrict detection to these entity types. Omit to run every recognizer. |
| allow_list | string[] | Exact strings suppressed before scoring — your own support address, test card numbers, and so on. |
| mode | "full" | "fast" = "full" | fast uses deterministic pattern recognizers only: lower latency, no contextual name detection. |
| include_text | boolean = false | Echo the matched substring in each entity. Off by default so detected values never travel back over the wire. |
Request body
Response
POST /v1/redact
Detects and transforms in a single pass. Returns redacted_text per document plus an audit trail of every operator applied.
| Parameter | Type | Description |
|---|---|---|
| text | string | A single document to analyze. Mutually exclusive with texts. |
| texts | string[] | A batch of documents processed in one call. |
| language | string = "en" | Analyzer language. See GET /v1/entities. |
| score_threshold | number = 0.35 | Minimum confidence, 0–1. Lower values find more entities with more false positives. |
| entities | string[] | Restrict detection to these entity types. Omit to run every recognizer. |
| allow_list | string[] | Exact strings suppressed before scoring — your own support address, test card numbers, and so on. |
| mode | "full" | "fast" = "full" | fast uses deterministic pattern recognizers only: lower latency, no contextual name detection. |
| include_text | boolean = false | Echo the matched substring in each entity. Off by default so detected values never travel back over the wire. |
| operators | object | Per-entity transformation map, keyed by entity type or DEFAULT. See Operators. |
Request body
Response
POST /v1/anonymize
Same shape as redact, intended for reversible encrypt operators. Your encryption key is used in memory for the request and never stored.
| Parameter | Type | Description |
|---|---|---|
| text | string | A single document to analyze. Mutually exclusive with texts. |
| texts | string[] | A batch of documents processed in one call. |
| language | string = "en" | Analyzer language. See GET /v1/entities. |
| score_threshold | number = 0.35 | Minimum confidence, 0–1. Lower values find more entities with more false positives. |
| entities | string[] | Restrict detection to these entity types. Omit to run every recognizer. |
| allow_list | string[] | Exact strings suppressed before scoring — your own support address, test card numbers, and so on. |
| mode | "full" | "fast" = "full" | fast uses deterministic pattern recognizers only: lower latency, no contextual name detection. |
| include_text | boolean = false | Echo the matched substring in each entity. Off by default so detected values never travel back over the wire. |
| operators | object | Per-entity transformation map, keyed by entity type or DEFAULT. See Operators. |
Request body
POST /v1/deanonymize
Reverses an encrypt anonymization. operators is required and must carry the same key.
| Parameter | Type | Description |
|---|---|---|
| text * | string | A previously anonymized document. |
| operators * | object | Per-entity transformation map, keyed by entity type or DEFAULT. See Operators. |
POST /v1/jobs
Queues a large batch and returns immediately with a job id. Use this for very large batches or when your client cannot hold a long-lived connection.
| Parameter | Type | Description |
|---|---|---|
| task * | string | detect, redact, anonymize or deanonymize. |
| text | string | A single document to analyze. Mutually exclusive with texts. |
| texts | string[] | A batch of documents processed in one call. |
| language | string = "en" | Analyzer language. See GET /v1/entities. |
| score_threshold | number = 0.35 | Minimum confidence, 0–1. Lower values find more entities with more false positives. |
| entities | string[] | Restrict detection to these entity types. Omit to run every recognizer. |
| allow_list | string[] | Exact strings suppressed before scoring — your own support address, test card numbers, and so on. |
| mode | "full" | "fast" = "full" | fast uses deterministic pattern recognizers only: lower latency, no contextual name detection. |
| include_text | boolean = false | Echo the matched substring in each entity. Off by default so detected values never travel back over the wire. |
Response
GET /v1/jobs/{id}
Polls a queued job. Jobs are scoped to the API key that created them and expire after 24 hours.
GET /v1/entities
Every entity type the service can detect, per language.
GET /v1/info
Languages, entity types, the request limits that apply to your key, and your rate card.
GET /v1/health
Returns operational, or 503 with degraded. Safe to poll from an uptime monitor.
GET /v1/usage
Current billing-period counters for the calling key: requests, documents, characters, amount charged and remaining allowances.
Response
Operators
Operators drive /v1/redact, /v1/anonymize and /v1/deanonymize. Keys are
entity types; DEFAULT catches everything not named explicitly.
| Type | Parameters | Behaviour |
|---|---|---|
| replace | new_value? | Substitutes a literal. Omit for the <ENTITY_TYPE> placeholder. Max 512 characters. |
| redact | — | Removes the span entirely. |
| mask | masking_char, chars_to_mask, from_end | Masks N characters with one character, from the start or the end. N ≤ 1000. |
| hash | hash_type | Deterministic sha256 / sha512 / md5 — preserves joinability without exposing values. |
| encrypt | key | AES with a 16, 24 or 32-byte key. Reversible through /v1/deanonymize. |
| keep | — | Leaves the span untouched — carve exceptions out of DEFAULT. |
Example
Data handling
- No document retention. Text exists only in memory for the lifetime of the request. It is never written to storage, logs or analytics.
- Metering is metadata-only — request counts, document counts, character counts, entity counts, latency and the amount charged.
include_textis off by default, so detected values never travel back over the wire unless you ask for them.- Encryption keys stay yours. They are used for the request and discarded; they are never logged or stored.
- TLS 1.2+ everywhere, terminated at the nearest edge location to your client.
Questions about data residency, DPAs or SOC 2 reporting: support@efficientstack.com.