# Detect and redact PII in text with ZeroGPU's GLiNER API

ZeroGPU's GLiNER models find personal data in text using labels you set on each request, such as `person`, `email` and `phone number`, with no training, and your code replaces what they return. With `gliner2.5-multi-v1`, input tokens cost $0.02 per million tokens and output tokens cost $0.05 per million tokens, drawn from prepaid Credits. Send `POST https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/zeroclick/v1/responses` with `model`, `input` and a `metadata` object that sets `usecase` to `ner`, your labels and a threshold.

## At a glance

- **Endpoint:** `POST https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/zeroclick/v1/responses`
- **Body:** `model` (`gliner2.5-multi-v1` or `gliner2.5-base-v1`), `input` (the text), and `metadata` with `usecase` (`ner` or `json`) plus `labels` and `threshold`, or a `schema`
- **Price:** `gliner2.5-multi-v1` input $0.02 per million tokens, output $0.05 per million tokens. `gliner2.5-base-v1` input $0.02 per million tokens, output $0.05 per million tokens.
- **Payment:** calls draw on prepaid Credits, bought once from $0.01 on a wallet or $0.50 by card, with `Authorization: Bearer <token>` on each call.
- **Response:** an OpenAI Responses object. `output[0].content[0].text` is a JSON string whose `entities` object has one key per label.
- **Limits:** one `input` string per request. `threshold` runs from 0 to 1 and defaults to 0.5.
- **Full purchase flow:** [the buyer skill](https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/SKILL.md)

## Choose the model and the use case

| Your text | `model` |
| --- | --- |
| English only | `gliner2.5-base-v1` |
| Multilingual or mixed languages | `gliner2.5-multi-v1` |

| Goal | `metadata.usecase` | Fields it uses |
| --- | --- | --- |
| Find each piece of personal data by type | `ner` | `labels`, `threshold` |
| Pull one structured record, such as a contact | `json` | `schema` |

Labels are plain strings you choose for each request. Use `person`, `email`, `phone number` and `order number` for a support ticket, or `address` and `company` for a document. A lower `threshold` returns more matches, including lower-confidence ones.

## Find personal data in text and redact it

1. Get a credential that holds credit: Credits bought with a registered agent credential, or a receipt from a small credit bought without registering for a first test. [The buyer skill](https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/SKILL.md) has the steps.

2. Send the text and the labels:

   ```bash
   curl -s https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/zeroclick/v1/responses \
     -H "Authorization: Bearer $ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"model": "gliner2.5-multi-v1", "input": "Hi, this is Dana Example. Please call me back on +1 555 0100 or write to dana@example.com about order 4471.", "metadata": {"labels": ["person", "phone number", "email", "order number"], "usecase": "ner", "threshold": 0.3}}'
   ```

   This request returns the following response, trimmed to the fields you read:

   ```json
   {
     "object": "response",
     "status": "completed",
     "model": "gliner2.5-multi-v1",
     "output": [
       {
         "type": "message",
         "role": "assistant",
         "content": [
           {"type": "output_text", "text": "{\"entities\":{\"person\":[\"Dana Example\"],…"}
         ]
       }
     ],
     "usage": {"input_tokens": 27, "output_tokens": 31, "total_tokens": 58},
     "metadata": {"usecase": "ner"}
   }
   ```

   The example drew $0.000003 from credit.

3. Parse `entities` and replace each value in your own code. The `entities` object groups the values found under each label: in the example, each of the four labels matched a value, with `Dana Example` under `person`. Check that each returned value appears in the input before you replace it, and replace longer values first, so a short match cannot break a longer one:

   ```python
   import json

   def redact(text, response):
       found = json.loads(response["output"][0]["content"][0]["text"])["entities"]
       pairs = [(value, label) for label, values in found.items() for value in values]
       for value, label in sorted(pairs, key=lambda p: len(p[0]), reverse=True):
           if value in text:
               text = text.replace(value, f"[{label}]")
       return text
   ```

   With this function, `Dana Example` becomes `[person]`. Store or send the redacted string instead of the original.

## Extract a contact record with a schema

Set `usecase` to `json` and describe each field as `name::type::description`:

```json
{
  "model": "gliner2.5-multi-v1",
  "input": "Best regards, Dana Example, Support Lead at Example Co. Phone: +1 555 0100, Email: dana@example.com",
  "metadata": {
    "usecase": "json",
    "schema": {
      "contact": ["name::str::Full name", "title::str::Job title", "company::str::Company name", "phone::str::Phone number", "email::str::Email address"]
    }
  }
}
```

Read the result from `output[0].content[0].text`, as with `ner`.

## Cost and boundaries

- Each call is billed on its input and output tokens and draws on your Credits balance. For example, assuming 1,000 tickets of 500 input and 100 output tokens each, the draw on `gliner2.5-multi-v1` is $0.015.
- Every response paid from credit carries a `zc-billing` header with `chargedUsd` and `remainingCreditUsd`. Unused credit stays on your balance.
- The model finds entities. Replacing them is the step in your code shown above.
- Each request takes one `input` string.

## Errors

| Code | Meaning | Action |
| --- | --- | --- |
| `401 bearer_required` | The call carried no credential | Register at [auth.md](https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/auth.md) and retry with `Authorization: Bearer <token>`, or use a receipt |
| `403 plan_required` | The credential holds no Credits | Buy Credits, then retry |
| `usage_exhausted` | The Credits balance is used up | Top up with `POST https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/extend` and `{"amountUsd": "<amount>"}` |
| `receipt_credit_exhausted` | The receipt's balance cannot cover the call | Buy a new receipt, or buy Credits with a credential |
| `400` | The JSON is malformed, or `model` or `input` is missing | Fix the body |
| `429` | You hit the rate limit | Wait at least `retry-after` seconds, then retry |

## Frequently asked questions

### Which labels can I use for PII detection?

You can use any strings you choose, set on each request. The models are zero-shot: `person`, `email`, `phone number`, `address` or `order number` all work without training, and each label you send becomes a key in the returned `entities` object.

### Does ZeroGPU's API return redacted text?

No. The API returns the values it found, grouped under each label, and redaction is a replace step in your own code. The response is a JSON string in `output[0].content[0].text`, so you can parse `entities` and replace each value with a placeholder such as `[email]`.

### Can I detect personal data in non-English text?

Yes. Use `gliner2.5-multi-v1` for multilingual or mixed-language text. `gliner2.5-base-v1` handles English. Both take the same request body.

### Do I need a ZeroGPU API key?

No. Calls carry a ZeroClick agent credential as `Authorization: Bearer <token>`, which you can register for free, and that credential holds your Credits. As an alternative, you can send a receipt from a small credit bought without registering.

### How much does PII detection cost?

On `gliner2.5-multi-v1`, input tokens cost $0.02 per million tokens and output tokens cost $0.05 per million tokens. Each call's cost is drawn from prepaid Credits, which you buy once and top up as you go.

## Keep reading

- [Buyer skill](https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/SKILL.md): the full purchase flow
- [How to pay](https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/payment.md): Credits, rails and top-ups
- [ZeroGPU models by task](/guides/models-by-task)
- [ZeroGPU API pricing: per-token rates and Credits](/guides/pricing-for-agents)
- [ZeroGPU API errors and recovery](/guides/errors)

---

Updated 2026-10-16. All ZeroGPU guides: https://agents.zerogpu.ai/guides.md. Storefront: https://agents.zerogpu.ai/zcj/v1eqw7u3zq7k/llms.txt.
