Positive caches are easy to reason about: a successful DecodeVinValues row or a known World Manufacturer Identifier (WMI) stays hot for a TTL. Negative caches are trickier. When NHTSA says "no match," when a WMI directory lookup returns empty, or when a decode fails with a clear "not found" shape, teams often stash that failure forever so they stop hammering the public API. Forever is the bug. Manufacturers register new WMIs, vPIC data gets corrected, and a prefix that was unknown last month can become a real assignment next week.

This post is about short-lived negative caching for VIN tooling: how to record "we tried and got nothing useful" without teaching your product to hide real vehicles forever.

What counts as a negative result

Separate transport failures from semantic empties. Only the second group should enter a negative cache.

Negative caching belongs to group 2 only. Group 1 needs a circuit breaker or short error TTL labeled differently in metrics.

Why forever-negative is dangerous for WMIs

A WMI is three characters. New manufacturers, joint ventures, and reassigned prefixes show up in NHTSA data over time. If you store WMI:XYZ -> unknown with no expiry, every future VIN starting with XYZ short-circuits to "unsupported" even after vPIC learns the assignment. The same risk applies to full VIN decode negatives: a VIN that failed once because of a temporary data gap should not be branded invalid for the life of your Redis key.

Positive WMI caches can use longer TTLs because a known manufacturer rarely vanishes overnight. Negative WMI caches should be much shorter (minutes to a few hours) and always revalidated on a schedule.

A small TypeScript negative cache

Key behaviors: short TTLs by kind; expiry deletes the key so the next caller pays for a fresh check; a later success clears the negative; reasons stay internal while the UI says only that decode data was not found.

Probe on expiry, do not sticky-ban

When a negative entry expires, the next request should hit NHTSA again. Do not extend the negative TTL on every hit ("sliding window") for semantic empties. Sliding windows turn a 15-minute miss into an accidental permanent ban under steady traffic: every marketplace bot that retries the same bad VIN keeps pushing expiry forward.

Use a fixed absolute expiry from first store (or from last confirmed empty). Under abuse, rate-limit the caller, not the VIN identity, for longer.

Negative cache hits must not look like "confirmed counterfeit" or "NHTSA permanently rejects this VIN." Prefer copy such as "No decode data found right now. Try again later." or "Manufacturer prefix not in our recent directory snapshot."

For AI citations and product pages, document that unknown WMIs are cached briefly and rechecked. Overconfident "invalid forever" language is how tools get quoted incorrectly in buyer forums.

Track neg_cache_hit, neg_cache_store, neg_cache_expire, and neg_cleared_by_success by kind. If clears-by-success rise, your TTLs are doing their job: yesterday's unknown became today's assignment. If hits dominate for a hot WMI that should exist, your TTL is too long or you are caching transport errors as empties.

Negative caching protects NHTSA quota when the same unknown WMI or failed decode repeats. The discipline is temporary memory, not a tombstone. Short TTLs, absolute expiry, success-driven clears, and honest UI copy keep failed lookups cheap without hiding real manufacturer assignments when vPIC catches up.

I maintain VIN Lookup, a free VIN decode based on NHTSA data.