Response caching

Response caching stores the whole answer at the exchange. An identical request later gets that answer back, and no provider runs. A hit costs nothing.

It is off by default. Send x-liquid-cache: true to use it for one request. It works on Chat Completions, Messages and Responses, with and without streaming.

curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer sk-liquid-..." \
  -H "x-liquid-cache: true" \
  -H "x-liquid-cache-ttl: 3600" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-oss-120b", "messages": [{"role": "user", "content": "Name three rivers."}]}'

The first request returns x-liquid-cache-status: MISS and runs a job that you pay for. The same request within the hour returns HIT and costs nothing. In Python, read the status from the raw response:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://router.inference.ai.exchange/v1",
    api_key=os.environ["LIQUID_API_KEY"],
)

raw = client.chat.completions.with_raw_response.create(
    model="gpt-oss-120b",
    messages=[{"role": "user", "content": "Name three rivers."}],
    extra_headers={"x-liquid-cache": "true", "x-liquid-cache-ttl": "3600"},
)
print(raw.headers["x-liquid-cache-status"])  # MISS, then HIT
completion = raw.parse()

Before you turn it on

Prompt caching Response caching
What it stores The start of a long prompt, at the provider The whole answer, at the exchange
What a repeat request does Runs on a provider, with cheaper input Runs on no provider
Charge Cached input at a lower rate Nothing
Default On, with exchange.cache.mode: "auto" Off

What a hit returns

A hit returns the stored content again, with a new id and a new created time. Every usage counter is 0, and the charge is 0. The exchange block holds a cache object in place of the receipt of a job, because no job was bought. A hit has no x-liquid-job-id header, and it moves no money.

To delete the entry and store a new answer, send x-liquid-cache-clear:

client.chat.completions.create(
    model="gpt-oss-120b",
    messages=[{"role": "user", "content": "Name three rivers."}],
    extra_headers={"x-liquid-cache": "true", "x-liquid-cache-clear": "true"},
)

The request then runs, and you pay for it.

What decides a hit

A request hits an entry when all of these are the same:

The top-level fields prompt_cache_key and session_id do not count. They only name a conversation for the prompt cache of the provider. Headers do not decide a hit. A purchasing rule that you send as a header, such as x-liquid-cap-usd, does not change which entry the request finds.

Limits

Reference

Request headers

Request header Behaviour
x-liquid-cache true turns the cache on for this request. false or no header leaves it off.
x-liquid-cache-ttl Seconds to keep a new entry, from 1 to 86400. The default is 300.
x-liquid-cache-clear true deletes the entry for this request. The request then runs and stores a new entry.

A header that cannot be read returns 400 invalid_header. This includes a TTL outside 1 to 86400.

Rules

To use the cache for every request of a preset, set exchange.cache.response: true in the rules of the preset. The rules exchange.cache.response and exchange.cache.response_ttl_seconds can also appear in the exchange block of a request. They win over the headers. Thus response_cache: false in the request turns the cache off, also if you send x-liquid-cache: true. A x-liquid-cache-ttl header wins over exchange.cache.response_ttl_seconds. A rule TTL outside 1 to 86400 is moved into that range.

Response headers

Response header Value
x-liquid-cache-status HIT or MISS. Absent when the request did not use the cache.
x-liquid-cache-ttl The full TTL on a miss. The remaining TTL on a hit.
x-liquid-cache-age On a hit, the seconds since the entry was stored.
x-liquid-cache-source-job-id On a hit, the job that produced the stored answer.

Next steps