# Caching & freshness

URLpipe caches the result of every operation per URL. When you ask for the same thing again, you can accept the cached result — instantly, and without spending a single credit.

## The max\_age parameter

Every endpoint accepts an optional `max_age` parameter that says how fresh a cached result must be for you to accept it. If a cached result exists and is younger than `max_age`, URLpipe returns it as-is. Otherwise it does the work again and caches the new result.

- **Name**
  : `max_age`
  **Type**
  : string | integer
  **Description**
  : How old a cached result may be and still be accepted. Accepts a bare number of seconds (`3600`), or a human duration like `"2 hours"`, `"3 days"`, `"30m"` or `"45 sec"`. Defaults to `7 days`.

POST /markdown

```
# Accept a cached result up to 2 hours old
curl -X POST https://urlpipe.dev/markdown \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "max_age": "2 hours"}'
```

## Accepted units & limits

- Seconds: `s`, `sec`, `second(s)`
- Minutes: `m`, `min`, `minute(s)`
- Hours: `h`, `hr`, `hour(s)`
- Days: `d`, `day(s)`
- Weeks: `w`, `week(s)`

Values are clamped to a range of `0` to `30 days`. Set `max_age=0` to always bypass the cache and fetch fresh. An unrecognised value returns a `422` with error `invalid_max_age`.

## Cache hits are free

A cache hit runs no analysis, so it **neither spends credits nor is blocked by your allowance**. Even if you've hit your monthly limit, cached results keep flowing.

This makes `max_age` a direct cost lever. Polling a set of URLs every few minutes but happy with hourly freshness? Set `max_age="1 hour"` and most requests are served from cache for free. Need the absolute latest? Use `max_age=0` and every request does real work and spends credits.

A result still on its way counts too: repeat a request while the first is running and the repeat waits for it, for free. See [retries & duplicates](https://urlpipe.dev/docs/retries).

## Telling a hit from a miss

Every response says which it was. `X-Cache` is `hit` or `miss` (or `partial` for a [/scrape](https://urlpipe.dev/docs/scrape) whose operations differed), and on a hit `X-Cache-Age` gives the served result's age in seconds — always at or below the `max_age` you asked for. Together they tell you exactly what a given `max_age` is buying you before you tune it. See [Response headers](https://urlpipe.dev/docs/response-headers).

## What counts as the same request

Cached results belong to your **organization**, and every project in it draws on the same cache — one project's fetch is the next project's free hit. They go no further than that: your results answer your requests and nobody else's, and no other account's results are ever handed to you.

Within your organization, a cached result is keyed by the **operation**, the **URL**, and any **request options** that change the output. For [/lighthouse](https://urlpipe.dev/docs/lighthouse), for example, the `device` and `include_audits` options are part of the key, so a mobile audit and a desktop audit are cached separately.

[residential](https://urlpipe.dev/docs/residential) is part of the key too, and for the same reason: a site that answers a home address differently is the whole point of asking for one, so a residential result is never handed to a request that did not ask for it, and an ordinary result is never handed to one that did. Repeat the same residential call inside your window and it is a free hit like any other.

So is the project's [robots.txt setting](https://urlpipe.dev/docs/robots-txt): a page fetched by a project that does not follow robots.txt is never handed to one that does.

Source: https://urlpipe.dev/docs/caching
