Skip to content

Developer guide · Google Gemini

How to mask personal data in Gemini API requests

Maskflare EngineeringPublished 6 min read

Short answer

Mask personal data before each generateContent call and restore it in the response. On the Gemini API this matters twice: free-tier prompts can be used to improve Google's products and read by human reviewers. Write your own masking for a few formats, use Presidio, or point the Google Gen AI SDK at a masking gateway.

Gemini's long context makes it easy to send a whole mailbox export or a full customer file in one call, and its free tier makes it easy to start without a billing account. Put those together and you get the classic prototype: an engineer tests summarization on real customer emails with a free API key. That's the one setup where Google's terms say your prompts may be used to improve its products and read by people.

Paid keys are better, not clean. Prompts are still retained for abuse monitoring, and a European team still has to account for a transfer to a US provider. This guide covers what Google keeps, then three ways to mask the data before it goes: a small reversible masker of your own, Presidio, and a gateway in front of the Google Gen AI SDK.

How does Google treat Gemini API data?

As of October 2026, the Gemini API Additional Terms (last updated April 28, 2026) split everything by whether the service is paid:

Unpaid servicesPaid services
What countsGoogle AI Studio and unpaid quota in the Gemini APIGemini API through a Cloud project with an active billing account
Used to improve Google's productsYes, including machine-learning technologiesNo, prompts and responses aren't used to improve products
Human reviewReviewers may read, annotate and process input and outputOnly flagged content, for abuse monitoring
Google's own advice"Do not submit sensitive, confidential, or personal information"Logged for a limited period to detect policy violations
  • Abuse monitoring keeps data for 55 days. Google's abuse monitoring page says prompts, contextual information and outputs are retained for 55 days and used only for policy enforcement.
  • Users in the EEA, Switzerland and the UK. You may only use paid services when making API clients available to users there, and the paid-services data terms apply to those users even on free quota.
  • Some features always store data. Grounding with Google Search keeps prompts and output for 30 days with no way to turn it off, and the Files API keeps uploads until you delete them.
  • Guaranteed zero retention means Vertex AI. Google's zero data retention page recommends Vertex AI for workloads that need guaranteed zero retention or enterprise data processing agreements.

Check the current terms before relying on any of this. The practical rule is simple: never send personal data to a free key, and on paid keys, still send only what the task needs.

Where does personal data sit in a Gemini request?

  • `contents` and their `parts`. The user's text, plus every earlier turn: a chat session re-sends its history with each message.
  • `systemInstruction`. Templates that interpolate the customer's name, account or plan.
  • Function calls and responses. Gemini asks your code for a customer record, and the functionResponse part carries it back on the next turn.
  • Long-context documents. A pasted mailbox export or contract is usually where most of the personal data is, because nobody reads a few hundred pages before sending them.

Whatever does the masking has to read all of these, not just the newest user text.

Option 1: mask it yourself, reversibly

If you only care about one or two data types, you can mask reversibly with a dictionary that lives for the request. Here's email masking with stable placeholders, so the same address gets the same placeholder every time it appears:

Python: reversible email masking with an in-memory vault
import re
import secrets

EMAIL = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")

def mask_emails(text: str, vault: dict[str, str]) -> str:
    seen: dict[str, str] = {}

    def repl(m: re.Match) -> str:
        value = m.group()
        if value not in seen:
            placeholder = f"EMAIL_{secrets.token_hex(4)}"
            seen[value] = placeholder
            vault[placeholder] = value
        return seen[value]

    return EMAIL.sub(repl, text)

def unmask(text: str, vault: dict[str, str]) -> str:
    for placeholder, value in vault.items():
        text = text.replace(placeholder, value)
    return text

vault: dict[str, str] = {}
prompt = mask_emails("Draft a follow-up to maria.rossi@example.it", vault)
# "Draft a follow-up to EMAIL_9f2c41ab"

This works, and it shows where the real work is. The vault is in memory, so it's gone after a restart and can't serve a second process. Nothing expires it. Streamed answers can split EMAIL_9f2c41ab across chunks, and unmask as written only handles complete text. And email is the easy case: it has a shape. Phone numbers, national IDs and names don't, or have dozens of shapes.

Option 2: Presidio before generateContent

Presidio can analyze and anonymize the contents you pass to generate_content, and decrypt placeholders in the response if you used its encrypt operator. You'll walk Gemini's contents and parts structure yourself, along with system instructions and function-call parts. It's a good fit for batch jobs; for live traffic, budget for streaming and multi-language tuning. See the Presidio comparison.

Option 3: point the Google Gen AI SDK at a gateway

With Maskflare, you set the SDK's base URL to the gateway's Gemini route and use a Maskflare key where the Gemini key went. The gateway reads it from the same places the SDK sends a Gemini key, the x-goog-api-key header or the key query parameter, and recognizes it by its mf_ prefix:

Python: Google Gen AI SDK through Maskflare
from google import genai
from google.genai import types

client = genai.Client(
    api_key="mf_live_...",  # a Maskflare key
    http_options=types.HttpOptions(base_url="https://YOUR_GATEWAY_HOST/v1/gemini"),
)

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Draft a follow-up to maria.rossi@example.it about the CHF 2,400 credit note",
)
print(response.text)  # the real email address is back in the answer

# Streaming works the same way; tokens are restored as chunks arrive
for chunk in client.models.generate_content_stream(
    model="gemini-2.5-flash",
    contents="Summarize the thread with maria.rossi@example.it",
):
    print(chunk.text, end="")

The gateway masks every text field in the request body, including system instructions and function-call arguments, and decodes base64, hex and percent-encoded text before checking it. streamGenerateContent responses have tokens restored in streamed text and function calls when restoring is on for the key. Embeddings inputs (embedContent and batchEmbedContents) get deterministic tokens, so retrieval still matches. The TypeScript SDK takes the same two settings:

TypeScript: @google/genai through Maskflare
import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({
  apiKey: process.env.MASKFLARE_API_KEY!, // a Maskflare gateway key
  httpOptions: { baseUrl: 'https://YOUR_GATEWAY_HOST/v1/gemini' },
});

const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'Classify this complaint from maria.rossi@example.it',
});
console.log(response.text);

How do you roll it out safely?

Start with rules in alert mode. Requests pass unchanged while the traffic log counts what would have been masked, by rule and route, without storing the prompt. A few days of that usually settles the argument about whether the Gemini integration sends personal data. Then switch the rules to mask in development, check a sample of answers for tokens the model dropped or changed, and move to production with a separate mf_live_ key.

Two Gemini-specific checks are worth adding. First, list every API key in use and confirm each one belongs to a billed project, because a forgotten free key in a staging config is how unpaid-tier terms end up applying to real data. Second, if you use Grounding with Google Search, decide whether masked prompts being stored for 30 days is acceptable, since that storage can't be switched off.

Does masking make the free tier safe for personal data?

It changes what Google receives: tokens instead of names and emails. But read Google's terms as written. For unpaid services, Google asks you not to submit personal information at all, and context left in a masked prompt (a job title, a rare diagnosis, a small town) can still point at a person. Our advice: use a billed project for anything touching customer data, mask on top of it, and keep free keys for synthetic data. If your users are in the EEA, Switzerland or the UK, the terms already require paid services.

What are the limits?

  • Gemini API only, not Vertex AI. The gateway's Gemini route forwards to generativelanguage.googleapis.com. Vertex AI endpoints aren't a supported route, so mask in your own code with the SDK if you run on Vertex.
  • Images aren't read as text. A photographed document in a multimodal request isn't masked. Masking applies to the text in the request.
  • Grounding still stores prompts. With Grounding with Google Search, Google stores the (masked) prompt for 30 days. Masking keeps identities out of that store; it doesn't stop the storage.
  • Detection is explainable. Patterns, checksums and word lists, plus a local named-entity layer for names. Test with your own data, especially names.

Frequently asked questions

Does Google train on Gemini API data?

As of October 2026, Google's terms say paid-service prompts and responses aren't used to improve its products, while unpaid usage, such as free quota and AI Studio, can be used and reviewed by people. Check the current Gemini API Additional Terms.

When does my Gemini API usage count as paid?

When you call the API through a Cloud project with an active billing account. For users in the EEA, Switzerland and the UK, the paid-services data terms apply to all usage.

How long does Google keep Gemini API prompts?

Google's abuse monitoring page says prompts, context and outputs are retained for 55 days for policy enforcement. Grounding with Google Search stores prompts and output for 30 days.

How is the Maskflare key sent with the Gemini SDK?

In the same place the SDK sends a Gemini key: the x-goog-api-key header or the key query parameter. The gateway recognizes Maskflare keys by their mf_ prefix.

Can I use masked text for Gemini embeddings?

Yes. Embeddings inputs are masked with deterministic tokens, so the same value always becomes the same token and similarity search still works on masked text.

About the author

Maskflare Engineering

Guides from the engineers who build Maskflare's gateway, detection engine, desktop agent and SDKs. Every code sample is run against the product before it's published, and every limit we mention is one we hit ourselves.

Keep reading

See it on your own data

Try Maskflare free.
Bring your hardest prompts.

28-day free trial or 10,000 requests, whichever comes first. Point one app at the gateway or enroll a few laptops,
and watch what gets masked. Or talk to us first.