Caching
GroundRoute caches results so repeated and similar queries return without re-hitting an engine. A cache hit costs nothing from the engine — and under gain-share pricing you pay only 50% of what the hit saved you. Every response reports the cache outcome in cache_meta.
Tiers
GroundRoute looks up the cache in this order; the first hit wins.
Tier (cache_tier) | What it is |
|---|---|
exact_private | An exact, per-tenant match — same normalized query and parameters, for your tenant only. |
exact_pooled | An exact match served from the cross-customer pooled cache, when the query is safe to share (generic, no PII, not personalized, not BYOK-private). |
semantic | A near-match found by embedding similarity above the per-class threshold. Skipped for fresh queries. |
miss | No cache hit — the query was routed to an engine. |
The pooled cache is the durable edge: a generic query another customer already ran can serve yours for free. Private and BYOK queries are never pooled.
What goes into the cache key
The exact-cache key is derived from the normalized query plus the parameters that change the results: max_results, domains, lang, country, and the freshness bucket. Two requests that differ on any of these are cached separately. The key stored in cache_meta is a SHA-256 hex digest — the raw query is never stored in the key.
TTLs
Exact private TTLs are set per query class:
| Class | Private TTL |
|---|---|
news | 5 minutes |
answer | 3 hours |
web | 24 hours |
page | 5 days |
academic | 14 days |
fresh intent caps the exact TTL at 5 minutes regardless of class, and bypasses the semantic tier entirely.
Pooled TTLs are always ≤ the private TTL for the same cell (a tighter staleness budget for cross-tenant reuse), and depend on both the class and the freshness bucket (static / semi). Cells not in the pooled allowlist are not poolable.
cache_meta fields
| Field | Type | Meaning |
|---|---|---|
cache_tier | enum | miss, exact_private, exact_pooled, or semantic. |
poolable | bool | Whether this entry is eligible for the pooled cache. |
poolability_reason | string | Why it is/isn't poolable — e.g. poolable:web/static, byok_private, fresh_intent, news_class, pii_in_query, personalized, tenant_consume_opt_out, tenant_contribute_opt_out, cell_not_allowlisted, eval_error. |
screens_post_nfkc | bool | Safety screens ran on the NFKC-normalized query (required for any pooled entry). |
freshness_bucket | string | hourly, daily, or none. |
cache_key_private | string | null | SHA-256 hex of the private key (no raw query). |
cache_key_pooled | string | null | Pooled key — set only when poolable. |
cached_at | datetime | null | When the cached entry was written (hits only). |
ttl_seconds | int | null | Remaining/assigned TTL. |
cost_avoided_usd | float | Provider cost saved by this hit. Drives the gain-share charge. |
similarity | float | null | Semantic tier only — similarity vs the per-class threshold. |
Opting out of pooling
A tenant can opt out of consuming from the pool, contributing to it, or both. The reasons are distinct in the audit trail (tenant_consume_opt_out vs tenant_contribute_opt_out). When evaluation fails for any reason, the entry is forced private (eval_error) — the pooled cache fails closed.