# Without storage

Convert a file without storing it. The file comes in the request and the result in the response; afterwards only the time, formats, sizes and credits remain.

With `store=false`, switchfile converts a file without storing it. The file comes in the request, the result goes back in the response. Neither is written to our file storage or to the database, and the conversion cannot be fetched again later. The working copy on the server, with everything the conversion wrote, is deleted as soon as the conversion has ended, also when it fails, runs into the time limit or the request is cancelled.

## What is kept

For billing and to stop abuse, each conversion leaves one entry in the usage history:

| Kept | Not kept |
| --- | --- |
| Time of the request and when it ended | The file and the result |
| Account and API key | The file name |
| Input and output format | Options and `tag` |
| Size of the file and of the result | Webhook URLs |
| Processing time and credits, or the error code if it failed | Anything in the logs about the file |

The entry shows up in the dashboard like other conversions, without a name. It is kept for 400 days like all usage history, see the [privacy policy](https://switchfile.io/privacy#account).

## Simple API

Add `?store=false` to [POST /v1/convert](https://switchfile.io/docs/reference/post-v1-convert) and send the file as `multipart/form-data`. The response is the converted file itself with `200`, so there is no `wait` and no polling:

cURL:

```bash
curl "https://api.switchfile.io/v1/convert?store=false" \
  -H "Authorization: Bearer $SWITCHFILE_API_KEY" \
  -F file=@invoice.docx -F to=pdf \
  -o invoice.pdf
```

Node.js:

```js
import { readFile, writeFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("invoice.docx")]), "invoice.docx");
form.append("to", "pdf");
const res = await fetch("https://api.switchfile.io/v1/convert?store=false", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SWITCHFILE_API_KEY}` },
  body: form,
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).code}`);
await writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));
```

- A conversion with several result files, such as one image per page, comes back as one ZIP file.
- `X-Conversion-Id` names the entry in the usage history, `X-Credits` the credits it used.
- `X-Notes` is set when there are notes on the result, as a JSON array. An [incomplete result](https://switchfile.io/docs/errors#incomplete) has `X-Incomplete: 1` and names the missing pages in the notes. The first 5 of a day have `X-Credits: 0`.
- The header `Switchfile-Store: false` does the same as `?store=false`.
- Storage is needed for `url`, `upload_id` and `from`, so this mode takes only an uploaded file.
- A failed conversion comes back as [problem JSON](https://switchfile.io/docs/errors#v1) with its code.

## Jobs

Send `"store": false` to [POST /sync/v2/jobs](https://switchfile.io/docs/reference/post-v2-jobs). The job has three tasks, one of each: `import/base64`, `convert` and `export/url`. The answer is the finished job, and the export task holds each file as base64 in `content` instead of a URL:

```bash
curl https://api.switchfile.io/sync/v2/jobs \
  -H "Authorization: Bearer $SWITCHFILE_API_KEY" \
  -H "Switchfile-Store: false" \
  -H "Content-Type: application/json" \
  -d "{
    \"store\": false,
    \"tasks\": {
      \"import-1\": {\"operation\": \"import/base64\", \"file\": \"$(base64 < invoice.docx | tr -d '\n')\", \"filename\": \"invoice.docx\"},
      \"convert-1\": {\"operation\": \"convert\", \"input\": \"import-1\", \"output_format\": \"pdf\"},
      \"export-1\": {\"operation\": \"export/url\", \"input\": \"convert-1\"}
    }
  }"
```

```json
{
  "data": {
    "status": "finished",
    "tasks": [
      {
        "name": "export-1",
        "operation": "export/url",
        "status": "finished",
        "result": { "files": [{ "filename": "invoice.pdf", "size": 48213, "content": "JVBERi0xLjcK..." }] }
      }
    ]
  }
}
```

With `"redirect": true`, the response is the file itself, or a ZIP file for several results. The job ID is in `X-Job-Id`. A failed job comes back as JSON with the failed task, as usual.

> Send the header `Switchfile-Store: false` as well. Without it, the request body may be at most 16 MB, which is a file of about 11 MB as base64. With the header, files up to 50 MB fit.

Only `POST /sync/v2/jobs` works without storage: the result has to come back in the same request. Asynchronous jobs under `/v2/jobs` are rejected with `422`, and so are webhooks and any import or export operation other than the ones above.

## For the whole account

Turn it on for every request of your account with [PATCH /v1/account](https://switchfile.io/docs/reference/patch-v1-account):

```bash
curl -X PATCH https://api.switchfile.io/v1/account \
  -H "Authorization: Bearer $SWITCHFILE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"no_store": true}'
```

From then on every conversion of the account runs without storage: a request to `POST /v1/convert` or `POST /sync/v2/jobs` needs no `store=false`. Requests that would store a file fail with `403 STORAGE_DISABLED`, for example uploads, batches, ZIP archives, accessibility checks and task retries. A request with `store=true` fails with `422`. `GET /v1/account` shows the setting in `no_store`, and `max_file_size` then shows the limit of this mode. `{"no_store": false}` turns it off again.

Conversions and jobs created before you turned it on store nothing more. A task that has not run yet fails with `STORAGE_DISABLED`, and so does an `import/upload` task that gets its file afterwards; that file is deleted right away. Files stored before stay until they are deleted as usual. The setting covers requests with the API keys of your account. Conversions on the switchfile.io website run under the website's own key and are kept as the [privacy policy](https://switchfile.io/privacy#files) describes.

> In the SDKs, keep calling the methods for this mode, such as `convertWithoutStorage`, and send jobs with `store: false`. The other methods expect a stored conversion, so the account setting rejects them.

## Limits

| Limit | Value |
| --- | --- |
| File | 50 MB |
| All result files together | 100 MB |
| Time to upload the file | 120 seconds |
| Time for the conversion | 90 seconds |
| Time to download the response | 180 seconds, then the connection is closed |
| Requests at the same time | 2 per account |

A request can take up to 390 seconds from the first byte of the upload to the last byte of the result. The SDKs wait that long in this mode. If you call the API yourself, set your HTTP client's timeout to at least 390 seconds.

What does not work in this mode, and why:

- Webhooks: the response already holds the result, and there is nothing to fetch later.
- `Idempotency-Key`: a repeated request cannot return a result that was not kept. Send the request again instead.
- `X-End-User`: visitor quotas of service keys need stored conversions.

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| 403 | `STORAGE_DISABLED` | The account converts without storage, and this request would store a file |
| 408 | `UPLOAD_TIMEOUT` | The file did not arrive within 120 seconds. Send it again over a faster connection |
| 413 | `FILE_TOO_LARGE` | The file is larger than 50 MB |
| 422 | `RESULT_TOO_LARGE` | The result is larger than 100 MB. Convert to a smaller format or split the file |
| 422 | `TIMEOUT` | The conversion took longer than the time limit |
| 429 | `CONCURRENCY_LIMIT` | Two requests of the account are already running. Wait for one of them |
| 503 | `SERVER_BUSY` | The server is busy. Try again after `Retry-After` seconds |
| 503 | `NO_STORE_UNAVAILABLE` | Converting without storage is not available right now. Try again later |

The other codes are the same as for stored conversions, see [Errors](https://switchfile.io/docs/errors).

## How the request travels

Like every request to `api.switchfile.io`, a request without storage passes through the network of Cloudflare, which ends the encrypted connection and forwards the request to our server in the EU (see the [privacy policy](https://switchfile.io/privacy#hosting)). Responses without storage carry `Cache-Control: no-store`, so they are not cached on the way. On our server, the file and the result are processed in memory while the request runs.

## Next

- [Sync](https://switchfile.io/docs/sync): Wait for a conversion in the same request. ?wait in the simple API, the /sync prefix and wait endpoints for jobs.
- [Limits and quotas](https://switchfile.io/docs/limits): Rate limits, file sizes, time limits, the free daily quota, quotas per visitor for service keys and how long files are kept.
