What to check
- ✓Read
usage.prompt_tokens_details.cached_tokens. Above zero is a hit. Zero means this call missed. Implicit cache does not guarantee a hit, and it does not charge storage. - ✓Storage starts only when the request sets
"caching": {"type": "enabled"}. It runs from creation until you delete that cache by ID orexpire_atpasses. Zero calls do not prove the column is zero. - ✓
expire_attops out at 7 days: current UTC time plus 604800 seconds. Another hit does not push that timestamp forward. After expiry you create the cache again. - ✓A prefix cache under 256 input tokens, or with
streamtrue, errors. It does not skip quietly. On create,output_tokensis always 0. Zero output is not a free call. - ✓Deleting one turn makes later turns recompute as fresh input and store again on the next request. During a version switch the hit fails and storage still bills.
1. The discount and the storage clock are different columns
Volcengine Ark splits context cache into two paths on the current context cache page. Implicit cache turns on by itself and cannot be turned off. A hit is not guaranteed, and storage is free. The only signal is cached_tokens: above zero uses the cache-hit price, zero uses the normal input price, and output stays on the normal output price. The hit price also changes with the input-length band, so the same model is not one rate below 32K and above 128K. The minimum length changes by model. A common default is 1024. Some models use 256, some 2048, and one sits above eight thousand. Memorizing the model list as a price sheet is wasted work. The page wins when it changes. cached_tokens at zero means this call did not enter the cache.
Explicit cache is something you create. The docs say the hit rate is higher and the control is tighter, and the cost is a storage column. Four lines: input, cached input, cache storage, and output. Cached input is priced well below new input. Storage is priced per thousand tokens per hour. It does not subtract from the hit discount. Once a request uses explicit cache, implicit cache does not apply. The two paths do not stack on one call. Before any of this, the model’s “inference (cache) pricing” has to be enabled on the activation page. Otherwise this is not automatic savings. The feature is simply off.
Do not file these four lines with the other clocks already on this site. A knowledge base bills spec hours, embeddings, and rerank. Closing the chat does not stop it. Deleting the base does. Dedicated deploy and a model unit bill capacity or time, and the clock starts when the deployment is running or the order succeeds. DeepSeek and Qwen context cache is a prefix-hit discount. It does not have Ark’s hourly storage meter. Claude and OpenAI prompt cache is a write premium: the hit is cheap, writing the cache costs more than normal input, and a miss after the window means you pay the write again. There is no column that keeps charging every natural hour after creation. Ark explicit cache runs the other way. The write is not the premium. The expensive part is every hour the cache stays alive.
2. Storage starts at creation, and reuse does not extend it
The context cache page is blunt about storage. The fee starts when the cache is created and stops only when you delete it or it expires. It settles at each natural-hour boundary, and less than one hour rounds up to one hour. A stopped chat, a stopped script, or an hour with no requests does not zero that column while the cache still exists. Create at 13:50 and the 14:00 boundary bills that fragment as one hour. An empty bill for the current hour only means the hourly line has not posted. It is not proof the meter stopped.
expire_at covers both store and caching. The current maximum is 7 days, current UTC Unix time plus 604800. Past that moment the store expires, and using the cache again does not reset the lifetime. After expiry you recreate it through the Responses API. That is not the overseas prompt-cache rule where a hit inside the window refreshes the timer. Seven days here is a cap, not “keep using it and it never ends.” A session left alone, with nobody editing expire_at and nobody deleting, runs at most to day seven. A session whose expiry was set far out and then forgotten keeps adding a storage line every natural hour until that timestamp.
Two official pages do not describe the quantity inside that hour the same way. The context cache page says each turn’s newly cached tokens in that hour are summed, and storage is those new tokens times the storage price. The model service billing page says cache storage settles hourly from the maximum tokens stored in each natural hour, while a cache hit settles in near real time. The old Context API page is offline. It still talks about a maximum mark count for the hour and a TTL in seconds. Do not reconcile a Responses API bill against those retired fields. What the two current pages agree on is narrower: natural hours, under one hour rounds up to one hour, billing from creation, and a stop only on delete or expiry. Reconcile against the cache-storage line on the bill. Do not argue that line down because the hour had no calls. Hits post in near real time. Storage posts on the hour. They were never the same minute.
A version switch is the window most often read as an outage. The docs say that during the switch a Responses API request cannot hit the cache, but storage continues and the storage fee still accrues. After the switch, earlier turns can hit again. A hit rate of zero can sit next to a rising storage line. Reading “no hit” as “not billing” is how those hours get filed as a surprise charge.
3. Deleting one turn is not an unsubscribe
Deleting one turn by ID removes that turn’s cache record. It does not stop the storage clock for the cache that remains. The documented case deletes turn 3 after turn 5. On the next request, later turns are recomputed and stored again. At turn 6, the affected turns 4 and 5 bill as input, not as cached input, until a new cache exists. The first call after a delete looks like “the cache missed, so it cost more.” The reason is those tokens went through as new input and were written into storage again. Deleting a turn and then continuing the chat can cost more than leaving it, and it is not a storage refund.
Deleting the first turn has a second effect. Tools can be set only on the first turn. Later turns carry that tools cache as input and must not send tools again, or the request errors. Once the first turn is deleted, later turns no longer carry the tools cache, and tools cannot be configured again. A function-calling session that loses turn 1 does not just lose history. The tool definitions are gone. The next turn either cannot attach tools, or the cache has to be built from scratch.
Three other fields fail the hit and the write together. While the cache has not expired, the storage fee does not stop with them. thinking has to match the previous turn: auto on turn 1 means auto later, and an unset field on turn 1 means later turns stay unset. A non-empty instructions on this turn can neither read the existing cache nor write this turn into it. store defaults to true, and a write requires storage to be on. If any earlier turn omitted "caching": {"type": "enabled"}, later turns cannot write. Once any earlier turn enabled caching, json_schema is unsupported. json_object still is. All of these explain “this call got no discount.” None of them is “storage was unsubscribed.”
A prompt cache key is the field most often mistaken for a switch. It only biases implicit-cache routing so the same prefix prefers the same cache. It is not strongly consistent. Scale-out, a release, failover, an expired mapping, or a protection policy can route the request elsewhere. It does not create an explicit cache, it does not create an explicit storage fee, and it cannot move cached_tokens from zero to above zero. Adding a key to an implicit request does not stop an explicit cache that is still billing by the hour.
4. A short prefix errors, and zero output is not free
Explicit cache has two shapes. Prefix cache stores a fixed opening: a role, rules, a long document. The first turn needs "store": true, which is already the default, plus "caching": {"type": "enabled", "prefix": true}. Later turns pass previous_response_id. Creation requires at least 256 input tokens or it errors, and stream cannot be true. Implicit cache is the opposite kind of miss. Under its minimum it simply does not hit, cached_tokens stays zero, and the request still succeeds. A prefix under 256 fails creation. Creating a prefix on glm-5-3-flash-260828 also requires role to be user.
On a successful create, usage.total_tokens equals input_tokens, and output_tokens is always 0. A finance export that watches only output will call this an empty call. Input has already been priced as a miss or as cached input, and storage has started its hourly clock at that moment. Zero output is the normal shape of prefix creation. It is not “not billed.”
Session cache has no 256-token floor. It updates every turn. What gets stored is this turn’s input. The model reply is not written straight into the cache. The previous reply and the chain of thought become part of the next turn’s input, so input tokens grow. Context editing can cut the chain of thought and tool content. The hit is the overlap between this turn’s input and the edited cache. New input is input_tokens - cached_tokens. Do not treat the whole input_tokens figure as a miss. Storage accrues only when "caching": {"type": "enabled"}. The difference between session and prefix is the floor and whether the cache grows with the chat. It is not “session cache skips storage.” Both sit inside the same four columns.
5. Topping up the account does not delete the cache
Reconcile four columns. First, whether the request was implicit or explicit. Without caching.type = enabled there is no storage fee, only a possible hit discount. Second, whether expire_at is still inside the 7-day cap, and whether anyone assumed another call would extend it. Third, whether the cache ID was deleted. After one turn is removed, later turns may already have been recomputed as new input and stored again. Fourth, whether the cache-storage line on the bill is still increasing at the hour. Hits are near real time. Storage is hourly. An empty current hour is not a stopped meter. A version switch, or a changed thinking or instructions field, explains a miss. It does not explain why storage is still there.
Funding the Volcengine account is a fifth ledger. Moving stablecoins on-chain into assets that can top up the cloud account happens before that credit lands. It does not delete a cache ID, and it does not change expire_at. When that funding step is what you need, AllSwap is a no-signup, non-custodial swap. A completed swap means the assets moved. An explicit cache still has to be deleted by ID, and until expiry the storage fee still posts every natural hour.