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

> Send local files with a SQL statement in one HTTP request and query them with the upload:// URL scheme.

# Upload and query local files

The Firebolt HTTP API accepts `multipart/form-data` POST requests that carry a SQL statement together with one or more files (Parquet, CSV, ...).
The statement reads each file with the `upload://` URL scheme through table-valued functions such as [READ\_PARQUET](/reference-sql/functions-reference/table-valued/read_parquet), [READ\_CSV](/reference-sql/functions-reference/table-valued/read_csv), [READ\_AVRO](/reference-sql/functions-reference/table-valued/read_avro), and [READ\_TEXT](/reference-sql/functions-reference/table-valued/read_text).
This lets you query a file on your machine, or load it into a table, without staging it in Amazon S3 first.

Uploaded files exist on the engine's local disk only while the request runs and are deleted when it finishes.
Nothing is persisted unless the statement writes the data, for example with `INSERT INTO ... SELECT`.

## Request format

Send a POST request with a `Content-Type` of `multipart/form-data` to an engine, authenticated like any other [API request](/guides/developing-with-firebolt/using-the-api).
The request must contain:

* Exactly one part with the `name` directive set to `sql`, holding the SQL statement in UTF-8.
* Any number of file parts. Each file part needs a `filename` directive (the value is ignored) and a `name` directive that matches the regex `[_0-9a-zA-Z.-]+` and is unique within the request. The SQL statement references the part as `upload://<name>`.

With curl, `--form "sql=<statement>"` produces the statement part and `--form "<name>=@<path>"` produces a file part.

## Query an uploaded file

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl "https://<user engine URL>?database=<database>" \
  --header 'Authorization: Bearer <access token>' \
  --form "sql=SELECT * FROM READ_PARQUET('upload://my_file')" \
  --form "my_file=@./sales.parquet"
```

The statement references the part name (`my_file`), not the local file name (`sales.parquet`).
The `upload://` URL must match a part name exactly; glob patterns are not supported.

## Upload multiple files

A statement can reference any number of uploaded files, each by its own part name:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl "https://<user engine URL>?database=<database>" \
  --header 'Authorization: Bearer <access token>' \
  --form "sql=SELECT * FROM READ_PARQUET('upload://my_sales') CROSS JOIN READ_CSV('upload://my_targets', HEADER => TRUE, INFER_SCHEMA => TRUE)" \
  --form "my_sales=@./sales.parquet" \
  --form "my_targets=@./targets.csv"
```

## Load an uploaded file into a table

The `upload://` scheme works only in read table-valued functions.
[COPY FROM](/reference-sql/commands/data-management/copy-from) and [external tables](/reference-sql/commands/data-definition/create-external-table) reject it.
To persist uploaded data, insert the function's result into a table:

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
curl "https://<user engine URL>?database=<database>" \
  --header 'Authorization: Bearer <access token>' \
  --form "sql=INSERT INTO sales SELECT * FROM READ_PARQUET('upload://my_sales')" \
  --form "my_sales=@./sales.parquet"
```

## Compress parts

To reduce transfer size, compress individual parts and declare the codec with a per-part `Content-Encoding` header.
The engine decompresses each part on arrival.
Supported encodings are `gzip`, `deflate`, `br`, `xz`, `zstd`, `lz4`, and `snappy`.
Both file parts and the `sql` part can be compressed.

```bash theme={"theme":{"light":"css-variables","dark":"css-variables"}}
gzip targets.csv
curl "https://<user engine URL>?database=<database>" \
  --header 'Authorization: Bearer <access token>' \
  --form "sql=SELECT * FROM READ_CSV('upload://targets', HEADER => TRUE, INFER_SCHEMA => TRUE)" \
  --form "targets=@./targets.csv.gz;headers=Content-Encoding: gzip"
```

A top-level `Content-Encoding` header on the request is rejected; compress per part instead.

## Limits

* The file parts of one request can hold at most 1 GB of data in total, measured after decompression.
