> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache and ETag

> Avoid reconverting the same document with ETag and If-None-Match.

# Cache and ETag

## How to use it

<Steps>
  <Step title="Save the ETag">
    In the first response, read the header `ETag`.
  </Step>

  <Step title="Forward with If-None-Match">
    In the next identical request, send `If-None-Match: <etag>`.
  </Step>

  <Step title="Receive 304">
    If nothing changed, the API answers `304 Not Modified` without a body. You reuse your local copy.
  </Step>
</Steps>

## Example

```bash theme={null}
# First time: save the ETag
ETAG=$(curl -sD - -o savalida.md \
  -X POST "https://api.markpdf.tech/convert/raw?filename=report.pdf" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/pdf" \
  --data-binary "@report.pdf" | grep -i '^etag:' | cut -d' ' -f2 | tr -d '\r')

# Second time: if it didn't change, respond 304
curl -i -X POST "https://api.markpdf.tech/convert/raw?filename=report.pdf" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/pdf" \
  -H "If-None-Match: $ETAG" \
  --data-binary "@report.pdf"
```

## Cache-Control

The response marks `Cache-Control: public, max-age=..., immutable`. The `ETag` changes if the document or any parameter changes, so it is safe to cache aggressively.

<Tip>
  The `ETag` changes with the parameters. Converting the same PDF to `fast` and `balanced` produces different ETags.
</Tip>

## What goes into the ETag calculation

The `ETag` is a hash derived from the binary content of the document **plus** the
`response_format`. Parameters that do not change the resulting Markdown (for
example `filename`, which is only used to detect format and name the
answer) do not enter the hash.

| You change…                                | Does ETag change? |
| ------------------------------------------ | ----------------- |
| The content of PDF (even if it is 1 byte)  | Yes               |
| `input_format` (`auto` -> `pdf`)           | Yes               |
| `mode` (`fast` -> `balanced`)              | Yes               |
| `clean` (`true` -> `false`)                | Yes               |
| `filename` (same document, different name) | No                |
| Order of the query params in the URL       | No                |

## Real use cases

**Idempotent network retries.** If your client retries an upload because
the timeout expired but the conversion did finish on the server side, a
second attempt with `If-None-Match` avoids paying and processing the same
document with the same parameters.

**Ingestion pipelines with reprocessing.** If your pipeline reprocesses the same
batch of PDFs every night (for example, to regenerate embeddings) and most
documents did not change from the previous day, save the `ETag` per document.
It allows you to skip converting everything that didn't change.

**Debugging "why the output changed".** If you notice that the Markdown of a
document changed between two calls that you thought were identical, compare the ETags:
if they are different, some parameter (or the document itself) changed — it is a
quick way to discard "weird cache" and confirm that the entry really
It's different.

## Example with requests (Python)

```python theme={null}
import requests

API = "https://api.markpdf.tech/convert/raw"
headers = {"x-api-key": "YOUR_API_KEY", "content-type": "application/pdf"}

with open("report.pdf", "rb") as f:
    body = f.read()

# First call
r1 = requests.post(f"{API}?filename=report.pdf", headers=headers, data=body)
etag = r1.headers["etag"]

# Second identical call: use If-None-Match
headers_conditional = {**headers, "if-none-match": etag}
r2 = requests.post(f"{API}?filename=report.pdf", headers=headers_conditional, data=body)

assert r2.status_code == 304  # not reconverted, no body
```

## Troubleshooting

| Symptom                                                                          | Cause                                                                                                                                   | Solution                                                                                |
| -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| The `ETag` changes on each call even if the document and parameters are the same | The document itself varies at the byte level (e.g. a regenerated PDF with different internal timestamp even though it "looks the same") | Compare the raw PDF bytes with a hash (`sha256sum`) before assuming they are identical. |
| `If-None-Match` has no effect and always converts                                | The header is being sent with quotes or a different format than the one returned by the API                                             | Resend the exact value of the received `ETag` header, without modifying it.             |

<Note>
  `ETag` only saves reconversion on the server; you still pay
  the network cost of uploading the document again. To avoid that too, use
  [`/convert/from-url`](/docs/api/convert-from-url) with `output_url` or save your
  own local copy of Markdown indexed by ETag.
</Note>
