How to Use OpenAI's 12-Hour Prompt Cache With GPT-6.1 Sol
You have a 40,000-token playbook that rides along with every request, and your traffic arrives in bursts hours apart. The default cache lives 30 minutes, so it expires between bursts and you pay to write the playbook again each time. The 12-hour prompt cache, added to the Responses API on October 20, 2026, fixes that. By the end you will have a GPT-6.1 Sol call that writes a shared prefix once and reads it at a twentieth of the input price, plus a usage check that proves it.
What the 12-hour prompt cache costs on GPT-6.1 Sol
Per-million-token prices at short context (up to 272K input tokens), from the OpenAI pricing page and the prompt caching guide:
| Token type | Multiplier | Price | | --- | --- | --- | | Uncached input | 1x | $2.00 | | Cache write, 30 minutes | 1.25x | $2.50 | | Cache write, 12 hours | 2.5x | $5.00 | | Cache read, any lifetime | 0.05x | $0.10 |
A write rate is the whole charge for those tokens, not a surcharge. The 12-hour option works on GPT-5.6 and later, in the Responses API only; earlier models use prompt_cache_retention.
Step 1: Send a GPT-6.1 Sol request with a 12-hour breakpoint
Caching uses a request-level prompt_cache_options object and a prompt_cache_breakpoint on a content block. Set mode: explicit so only the prefix you mark is cached. Implicit mode also places a breakpoint at the end of the latest user message, so the question that changes every request gets written too.
from openai import OpenAI
client = OpenAI()
# Stable material only: policies, tool docs, reference data. At least 1,024 tokens.
SHARED_PREFIX = open('support_playbook.md', encoding='utf-8').read()
def prefix_message() -> dict:
return {
'role': 'developer',
'content': [
{
'type': 'input_text',
'text': SHARED_PREFIX,
'prompt_cache_breakpoint': {'mode': 'explicit', 'ttl': '12h'},
}
],
}
def ask(question: str):
return client.responses.create(
model='gpt-6.1-sol',
input=[prefix_message(), {'role': 'user', 'content': question}],
extra_body={'prompt_cache_options': {'mode': 'explicit'}},
)
response = ask('A customer wants a refund after 45 days. What do we do?')
print(response.output_text)
The breakpoint's ttl overrides the request default of 30m for that block. The breakpoint must sit in an input_text block of a developer or user message, not in top-level instructions. I use extra_body so the code does not depend on your SDK version.
Step 2: Read cache writes and reads from the usage fields
Usage splits writes by lifetime. Print it on every call while testing.
import json
def cache_report(response) -> dict:
usage = response.usage.model_dump()
details = usage['input_tokens_details']
writes = details['cache_write_tokens_details']
return {
'input_tokens': usage['input_tokens'],
'cached_tokens': details['cached_tokens'],
'cache_write_tokens': details['cache_write_tokens'],
'ttl_30m_tokens': writes['ttl_30m_tokens'],
'ttl_12h_tokens': writes['ttl_12h_tokens'],
}
print(json.dumps(cache_report(response), indent=2))
print(json.dumps(cache_report(ask('And after 90 days?')), indent=2))
The first call should show ttl_12h_tokens close to your prefix size and cached_tokens at zero. The second should flip that: cached_tokens near the prefix size and no new writes. If both stay at zero, your prefix is probably under 1,024 tokens or changes between calls.
Step 3: Work out the break-even point for your prefix
The guide's cost formula, with the GPT-6.1 Sol read multiplier of 0.05:
PRICE_PER_M = 2.00 # GPT-6.1 Sol input, USD per 1M tokens, short context
READ_X, WRITE_30M_X, WRITE_12H_X = 0.05, 1.25, 2.5
def input_cost(usage: dict) -> float:
d = usage['input_tokens_details']
w = d['cache_write_tokens_details']
cached = d['cached_tokens']
ordinary = usage['input_tokens'] - cached - d['cache_write_tokens']
weighted = (
ordinary
+ cached * READ_X
+ w['ttl_30m_tokens'] * WRITE_30M_X
+ w['ttl_12h_tokens'] * WRITE_12H_X
)
return weighted * PRICE_PER_M / 1_000_000
Do not add cache_write_tokens on top: it is already the sum of the two lifetimes.
For a 50,000-token prefix, uncached input costs $0.10 per request. A 12-hour write costs $0.25 once and each read costs $0.005, so the write pays for itself on the third request inside the window ($5.00 + 2 x $0.10 is below 3 x $2.00 per million tokens).
A day with nine requests, one per hour:
- No caching: 9 x $0.10 = $0.90
- 30-minute cache, if each request finds the entry expired: 9 x $0.125 = $1.125
- 12-hour cache: $0.25 + 8 x $0.005 = $0.29
A 30-minute write can cost more than no cache when traffic is sparse. If you expect fewer than three requests in 12 hours, or the prefix changes daily, skip the 12-hour option.
Step 4: Prewarm the 12-hour prompt cache before the first burst
Prewarming writes the cache without generating output, so your first real user gets a read instead of a cold start.
def prewarm() -> None:
client.responses.create(
model='gpt-6.1-sol',
input=[prefix_message()],
extra_body={'prompt_cache_options': {'prewarm': True}},
)
Run it from a scheduler shortly before traffic starts, then send normal requests with prewarm omitted. The prefix must render identically in both calls, or the real request will miss the warmed entry.
Gotchas
- Minimum size: the cacheable prefix needs at least 1,024 visible input tokens; hidden OpenAI system content does not count.
- Exact prefix match: put timestamps and user-specific text after the breakpoint, and keep tool names, schemas and ordering stable, since tools are cached too.
- Upgrades are charged again: raising a valid 30-minute entry to 12 hours bills a new 12-hour write with no credit for the old one.
- Prewarming is billed: if no traffic follows, you paid 2.5x for nothing.
- 12 hours is a minimum, not a schedule: the docs say a 30-minute read does not restart the 12-hour clock, so traffic spanning more than 12 hours needs a second write.
A 12-hour cache is a cost you can compute before launch and verify from the usage fields on every call, so it never surprises you on the production bill.