# Exporting data by SQL

Export the complete result of a [SQL](https://dev.nominal.io/sql-guides.md) query as a CSV file. Nominal runs the query without a row cap and returns a link to download the result. For advanced usage, see the REST API reference for [Export](https://dev.nominal.io/sql-reference/rest-api/export.md).

## In code

The Python, Rust, and Go examples call the SQL REST API directly. Python reads the REST API URL, API key, and workspace RID from your `default` profile. For Rust and Go, paste them in from the [API keys setup](https://dev.nominal.io/quickstart/api-keys-setup.md) page.

**Python**

```python
import requests
from nominal.config import NominalConfig

profile = NominalConfig.from_yaml().get_profile("default")

response = requests.post(
    f"{profile.base_url}/sql/v1/query/export",
    headers={"Authorization": f"Bearer {profile.token}"},
    json={
        "query": "SELECT ts, channel, value FROM points_double WHERE dataset_rid = '<ri.catalog.instance.dataset.identifier>'",
        "workspace_rid": profile.workspace_rid,
    },
)
with open("export.csv", "wb") as f:
    f.write(requests.get(response.json()["presignedUrl"]).content)
```

**MATLAB**

```matlab
client = nominal.Client.fromProfile();
url = client.queryExportUrl("SELECT ts, channel, value FROM points_double " + ...
    "WHERE dataset_rid = '<ri.catalog.instance.dataset.identifier>'");
websave("export.csv", url);
```

**Rust**

```rust
// needs the reqwest (with its json feature) and serde_json crates
let api_url = "<api url>";
let api_key = "<api key>"; // DO NOT COMMIT THIS
let workspace_rid = "<workspace rid>";

let response: serde_json::Value = reqwest::Client::new()
    .post(format!("{api_url}/sql/v1/query/export"))
    .bearer_auth(api_key)
    .json(&serde_json::json!({
        "query": "SELECT ts, channel, value FROM points_double WHERE dataset_rid = '<ri.catalog.instance.dataset.identifier>'",
        "workspace_rid": workspace_rid,
    }))
    .send().await?
    .json().await?;
let url = response["presignedUrl"].as_str().ok_or("no presignedUrl in the response")?;
std::fs::write("export.csv", reqwest::get(url).await?.bytes().await?)?;
```

**Go**

```go
import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
)

const apiURL = "<api url>"
const apiKey = "<api key>" // DO NOT COMMIT THIS
const workspaceRid = "<workspace rid>"

body, _ := json.Marshal(map[string]string{
	"query":         "SELECT ts, channel, value FROM points_double WHERE dataset_rid = '<ri.catalog.instance.dataset.identifier>'",
	"workspace_rid": workspaceRid,
})
req, _ := http.NewRequest("POST", apiURL+"/sql/v1/query/export", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
var out struct{ PresignedUrl string }
json.NewDecoder(resp.Body).Decode(&out)
result, _ := http.Get(out.PresignedUrl)
f, _ := os.Create("export.csv")
io.Copy(f, result.Body)
```

## In the Nominal app

1. Run the query on the **SQL** page, as in [Executing SQL](https://dev.nominal.io/quickstart/quick/quick-sql.md).
2. Click the **Export** icon, which appears once the query succeeds. Nominal runs the query again without a row limit and downloads the whole result as a CSV file.
