Reference

Caching API responses with the ttl parameter

A proxied GET normally does a fresh fetch to the target on every call, even if the target hasn't changed in a week. On Pro and Enterprise, adding ttl to the request caches the response at the edge for a while instead. This covers what gets cached, what goes into the cache key, and the parts that surprise people the first time they use it.

· ~5 min read

What ttl actually caches

Add ttl=<seconds> (1 to 86400, so up to 24 hours) to a GET request through the proxy, and a successful 200 response gets stored at the Cloudflare data center that served that request:

curl "https://api.corsproxy.dev/proxy?key=YOUR_KEY&url=https%3A%2F%2Fapi.example.com%2Fprices&ttl=300"

Only GET requests are eligible, and only a 200 upstream response gets stored; a 404, a 500, or anything that isn't GET is fetched fresh every time regardless of ttl. The parameter is Pro and Enterprise only. On Free, it fails before any fetch happens:

HTTP/1.1 403 Forbidden

{
  "success": false,
  "error": "Caching with ttl is a Pro feature. Upgrade at https://app.corsproxy.dev/dashboard/upgrade",
  "code": "PRO_FEATURE_REQUIRED"
}

Leave ttl off and nothing is cached: every request, on every plan, is a fresh call to the target. A value outside 1-86400, or anything that isn't a whole number, fails with a 400 INVALID_TTL before the request goes anywhere, Free or Pro.

What goes into the cache key

Two calls share a cached response only if they'd produce the same one. In practice, that means the cache key is built from your API key's ID, the exact target URL, and the headers the proxy would have sent upstream, after dropping ones that don't change the response: User-Agent, Accept-Encoding, Accept-Language, and a handful of others that vary per visitor or per request without the target caring. A custom header you add with reqHeaders is not dropped, so changing it (say, an API version header, or a per-tenant identifier you forward upstream) produces a different cache key and a fresh fetch, exactly as if you'd changed the URL.

Keying on the API key's ID also means caching is scoped per key, not per account or globally: two keys on the same account requesting the same URL with the same headers don't share a cached entry, and neither do two different customers' keys. That keeps one caller's cached response from ever being handed to another caller who happens to ask for the same target.

"Edge" means one data center, not everywhere

The cache lives in the Cloudflare data center that handled the request that populated it, not globally. A visitor in Frankfurt and a visitor in São Paulo, both hitting your API through the same key and the same ttl, each warm their own local cache; the first request from each region is still a MISS. That's a property of Cloudflare's caches.default, the same per-data-center cache the Worker also relies on for routing and other pieces of the request path, not something corsproxy.dev replicates between regions on top of it.

Every cached and fresh response carries an X-Cache header, HIT or MISS, so you can tell which one you got without guessing from latency alone.

A cache hit still counts against your quota

This is the one that catches people out: the daily request count is checked before the cache lookup happens, so a HIT uses one request the same as a MISS does. ttl saves the round trip to your target and whatever that target would have charged or rate-limited you for, not your own corsproxy.dev quota. See rate limits and quotas, explained for what the daily counter covers and how it resets.

When it's the wrong tool

A cached 200 is served as-is for up to ttl seconds, so it's a fit for data that's fine being briefly stale: a prices endpoint, a public leaderboard, a feed that updates every few minutes. It's a poor fit for anything that must reflect the instant it's requested, like a balance you're about to act on, or a response whose content is supposed to differ per call even when the URL and headers look the same (a one-time token, for instance): the first caller's response would be served to the next caller who matches the same cache key, 200 status and all. For those, leave ttl off.