Skip to content

Context Caching

Gunpal Jain edited this page Nov 15, 2026 · 1 revision

Context caching stores a large context once and reuses it across many requests. The context can be documents, files, conversation history or a system instruction. Cached tokens are billed at a lower rate than regular input tokens, and responses can come back faster. In this SDK, CachingClient creates and manages caches, and GenerativeModel.CachedContent attaches a cache to a model.

Package: Google_GenerativeAI. Namespaces: GenerativeAI.Clients (CachingClient) and GenerativeAI.Types (CachedContent, Duration).

This page documents caching on the Gemini Developer API, which is the platform covered by the SDK's caching tests.

Getting a CachingClient

Every GenerativeModel has a ready-made CachingClient property. You can also create a client from a platform adapter:

using GenerativeAI;
using GenerativeAI.Clients;
using GenerativeAI.Types;

var googleAi = new GoogleAi("YOUR_API_KEY");
var model = googleAi.CreateGenerativeModel(GoogleAIModels.Gemini38Flash);
CachingClient cachingClient = model.CachingClient!;

// Or standalone
var platform = new GoogleAIPlatformAdapter("YOUR_API_KEY");
var cachingClient2 = new CachingClient(platform);

Methods

Method Description
CreateCachedContentAsync(CachedContent) Creates a cache from contents, a system instruction, tools and a TTL or expiry
GetCachedContentAsync(name) Gets a cache by name (cachedContents/{id} or just {id})
ListCachedContentsAsync(pageSize, pageToken) Lists caches, one page at a time
UpdateCachedContentAsync(cacheName, cachedContent, updateMask) Updates ttl or expireTime (PATCH)
DeleteCachedContentAsync(name) Deletes a cache

All methods accept a CancellationToken.

CachedContent Properties

Property Notes
Model Required. Must match the model you'll use, for example GoogleAIModels.Gemini38Flash (models/gemini-3.8-flash)
Contents The content to cache (List<Content>)
SystemInstruction Optional Content
Tools, ToolConfig Optional tools stored with the cache
Ttl Duration, for example new Duration { Seconds = 3600 }, or TimeSpan.FromHours(1) through the implicit conversion
ExpireTime Timestamp. Use it instead of Ttl. Converts implicitly from DateTime
DisplayName Optional friendly name
KmsKeyName Customer-managed encryption key (a Vertex AI field)
Name, CreateTime, UpdateTime, UsageMetadata Output only

1. Creating Cached Content

var longDocument = await File.ReadAllTextAsync("path/to/long-document.txt");

var cachedContent = new CachedContent
{
    DisplayName = "Product Documentation Cache",
    Model = GoogleAIModels.Gemini38Flash, // must match the model you'll use later
    Contents = new List<Content>
    {
        new Content(longDocument, Roles.User)
    },
    Ttl = new Duration { Seconds = 3600 } // expires after 1 hour
};

CachedContent createdCache = await cachingClient.CreateCachedContentAsync(cachedContent);
Console.WriteLine($"Cache created: {createdCache.Name}");
Console.WriteLine($"Expires at: {createdCache.ExpireTime}");
Console.WriteLine($"Token count: {createdCache.UsageMetadata?.TotalTokenCount}");

With a system instruction

var cache = await cachingClient.CreateCachedContentAsync(new CachedContent
{
    DisplayName = "Customer Support Bot Cache",
    Model = GoogleAIModels.Gemini38Flash,
    SystemInstruction = new Content("You are a helpful customer support agent. Be professional and concise.", null),
    Contents = new List<Content>
    {
        new Content("Company policies and FAQs:\n\n[Large document content here]", Roles.User)
    },
    Ttl = TimeSpan.FromHours(2)
});

With multiple documents

var multiCache = await cachingClient.CreateCachedContentAsync(new CachedContent
{
    DisplayName = "Multi-Document Cache",
    Model = GoogleAIModels.Gemini38Flash,
    Contents = new List<Content>
    {
        new Content("Document 1: Product Specifications\n\n" + doc1, Roles.User),
        new Content("I've processed the product specifications.", Roles.Model),
        new Content("Document 2: User Manual\n\n" + doc2, Roles.User)
    },
    Ttl = new Duration { Seconds = 7200 }
});

Uploaded files can be cached as well. Add parts that reference a RemoteFile from Files Client.

2. Using Cached Content with GenerativeModel

Set the model's CachedContent property. Every request then sends the cache name.

var model = new GenerativeModel(platform, GoogleAIModels.Gemini38Flash)
{
    CachedContent = createdCache
};

var response1 = await model.GenerateContentAsync("What are the main features of this product?");
Console.WriteLine(response1.Text());

// Reuses the same cached context
var response2 = await model.GenerateContentAsync("What is the pricing structure?");
Console.WriteLine(response2.Text());

Console.WriteLine($"Cached tokens used: {response2.UsageMetadata?.CachedContentTokenCount}");

You can also attach an existing cache to a model you already have:

CachedContent? retrieved = await cachingClient.GetCachedContentAsync("cachedContents/your-cache-id");
model.CachedContent = retrieved;

When CachedContent is set, the SDK:

  • throws ArgumentException if model.Model is not exactly equal to CachedContent.Model;
  • removes Tools, ToolConfig and SystemInstruction from the request, because they must be part of the cache instead;
  • throws NotSupportedException if Google Search, code execution or legacy grounding is enabled on the model.

To set the cache per request instead, set GenerateContentRequest.CachedContent to the cache name.

3. Listing and Getting Caches

var cachedList = await cachingClient.ListCachedContentsAsync(pageSize: 10);

foreach (var item in cachedList.CachedContents ?? new List<CachedContent>())
{
    Console.WriteLine($"{item.Name} | {item.DisplayName} | {item.Model} | created {item.CreateTime} | expires {item.ExpireTime}");
}

if (!string.IsNullOrEmpty(cachedList.NextPageToken))
{
    var nextPage = await cachingClient.ListCachedContentsAsync(10, cachedList.NextPageToken);
}

var specificCache = await cachingClient.GetCachedContentAsync("cachedContents/your-cache-id");
Console.WriteLine($"Retrieved cache: {specificCache?.DisplayName}");

4. Updating a Cache (extend TTL)

Only ttl and expireTime can change after a cache is created. Use updateMask to name the field you're changing.

var updatedCache = await cachingClient.UpdateCachedContentAsync(
    cacheName: createdCache.Name!,
    cachedContent: new CachedContent { Ttl = new Duration { Seconds = 7200 } },
    updateMask: "ttl");

Console.WriteLine($"New expiration: {updatedCache?.ExpireTime}");

// Or set an absolute expiry
await cachingClient.UpdateCachedContentAsync(createdCache.Name!,
    new CachedContent { ExpireTime = DateTime.UtcNow.AddHours(4) }, "expireTime");

5. Deleting a Cache

await cachingClient.DeleteCachedContentAsync(createdCache.Name!);

Complete Example

using GenerativeAI;
using GenerativeAI.Clients;
using GenerativeAI.Exceptions;
using GenerativeAI.Types;

var apiKey = System.Environment.GetEnvironmentVariable("GOOGLE_API_KEY"); // "System." avoids a clash with GenerativeAI.Types.Environment
var platform = new GoogleAIPlatformAdapter(apiKey);
var cachingClient = new CachingClient(platform);

using var httpClient = new HttpClient();
var largeContext = await httpClient.GetStringAsync(
    "https://storage.googleapis.com/generativeai-downloads/data/a11.txt");

var cache = await cachingClient.CreateCachedContentAsync(new CachedContent
{
    DisplayName = "Apollo 11 transcript",
    Model = GoogleAIModels.Gemini38Flash,
    SystemInstruction = new Content("You are an expert on the Apollo program.", null),
    Contents = new List<Content> { new Content($"Transcript:\n\n{largeContext}", Roles.User) },
    Ttl = TimeSpan.FromMinutes(30)
});
Console.WriteLine($"Cache created: {cache.Name}, tokens: {cache.UsageMetadata?.TotalTokenCount}");

try
{
    var model = new GenerativeModel(platform, GoogleAIModels.Gemini38Flash) { CachedContent = cache };

    foreach (var question in new[]
             {
                 "Summarize the main events.",
                 "What problems did the crew report?",
                 "Describe the landing sequence."
             })
    {
        var response = await model.GenerateContentAsync(question);
        Console.WriteLine($"\nQ: {question}\nA: {response.Text()}");
    }
}
finally
{
    await cachingClient.DeleteCachedContentAsync(cache.Name!);
}

Error Handling

API errors throw ApiException. For example, you can retry once when rate limited:

using GenerativeAI.Exceptions;

async Task<CachedContent?> CreateCacheWithRetry(CachingClient client, CachedContent content)
{
    try
    {
        return await client.CreateCachedContentAsync(content);
    }
    catch (ApiException ex) when (ex.ErrorCode == 429)
    {
        await Task.Delay(TimeSpan.FromSeconds(5));
        return await client.CreateCachedContentAsync(content);
    }
    catch (ApiException ex)
    {
        Console.WriteLine($"Failed to create cache: {ex.ErrorCode} {ex.ErrorStatus} {ex.ErrorMessage}");
        return null;
    }
}

Getting or deleting a cache that doesn't exist also throws.

Key Benefits

  1. Cost savings: Cached tokens are billed at a lower rate than regular input tokens.
  2. Performance: The cached context isn't processed again on every request.
  3. Consistency: Every request uses the same context.
  4. Large context: You can cache content up to the model's context window.

Considerations

  • Model matching: CachedContent.Model must equal GenerativeModel.Model exactly. Use the same constant for both.
  • Tool restrictions: You can't combine Google Search, code execution or grounding with a cache on the model. Tools, tool config and system instructions belong in the cache.
  • Immutability: Content, system instruction, tools and model can't change after creation. Only ttl and expireTime can.
  • TTL management: Caches expire automatically. Extend them with UpdateCachedContentAsync.
  • Minimum size: Each model has a minimum number of tokens it will cache, and it differs between models. Check the Gemini caching docs for current limits.
  • Cost structure: Creating and storing a cache costs money (storage is billed per token-hour). Delete caches you no longer need.
  • Resource names: You can pass names as cachedContents/{id} or {id}.

When to Use It

Good fit: analysing large documents with many questions, RAG with a fixed corpus, multi-turn conversations over the same material, code-repository analysis, support bots with large knowledge bases, educational, legal or medical document Q&A.

Not a good fit: one-off queries, context that changes often, small contexts below the model's minimum, and very short sessions where the cost of creating the cache isn't recovered.

Cost Optimization Tips

  1. Ask as many questions per cache as you can, to spread the creation cost.
  2. Choose a TTL that matches how long you'll actually use the cache.
  3. Reuse caches for recurring workloads instead of creating new ones.
  4. Delete caches when you're done.

Related Pages

API Reference

Clone this wiki locally