# Query from an application

Run SQL over HTTPS with an Altertable SDK or HTTP client. You need [lakehouse credentials](/docs/administration/lakehouse-credentials.md) for the environment that owns your catalogs.

For terminal queries, use the [CLI](/docs/lakehouse/clients/cli.md). For all request fields, response formats, and errors, use the [HTTP API reference](/docs/api-references/lakehouse.md#post-query).

## Set up with your coding agent

```text
Add an Altertable query to this application. Read https://altertable.ai/docs/lakehouse/query/http-api.md and the linked SDK or HTTP reference first.

Inspect the application runtime and existing data-access code. Ask what result the application needs and confirm the target environment. Choose an SDK supported by this runtime, or use the HTTP API.

Keep lakehouse credentials on the server in the project's secret configuration. Verify authentication with SELECT 1 AS connected, then inspect the real catalog and table schemas. Use a bounded query and bind parameters for user-supplied values.

Integrate result handling and errors using the application's existing patterns. Handle the API's streamed response correctly. Run the relevant checks and verify a real result without logging credentials or sensitive rows. Summarize the code changes and required configuration.
```

## 1. Verify authentication

Set your credentials locally, then send a query that needs no existing table:

```bash
ALTERTABLE_USERNAME='YOUR_LAKEHOUSE_USERNAME'
ALTERTABLE_PASSWORD='YOUR_LAKEHOUSE_PASSWORD'
curl https://api.altertable.ai/query \
  --user "$ALTERTABLE_USERNAME:$ALTERTABLE_PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{"statement":"SELECT 1 AS connected"}'
```

Confirm that the response contains a `connected` column and a row with `1`. If authentication fails, check the credentials and target environment before querying application data.

## 2. Query your data

Replace the example catalog and table with names from your environment. The SDK examples show client initialization and result handling:

> **Before using the Altertable CLI:**
>
> Install the CLI if you haven’t already:
>
> ```bash
> curl -fsSL https://install.altertable.ai | sh
> altertable login
> ```
>
> If `altertable` isn’t found after installation, open a new terminal. Run `altertable login` to log in to your organization and environment. Follow the [CLI setup guide](/docs/lakehouse/clients/cli.md#2-log-in) to configure credentials and verify access.

### cURL

```bash
curl -X POST https://api.altertable.ai/query \
  -H "Authorization: Basic $ALTERTABLE_BASIC_AUTH_TOKEN" \
  -d '{"statement": "SELECT * FROM my_catalog.main.events ORDER BY timestamp DESC LIMIT 100"}'
```

### JavaScript

```typescript
import { AltertableLakehouseClient } from '@altertable/lakehouse';

const client = new AltertableLakehouseClient({
  username: '{{LAKEHOUSE_USERNAME}}',
  password: '{{LAKEHOUSE_PASSWORD}}',
});

const result = await client.queryAll({
  statement: `
    SELECT
      date_trunc('day', timestamp) AS day,
      event,
      COUNT(*)                     AS events,
      ROUND(AVG(value), 2)         AS avg_value
    FROM   my_catalog.main.events
    WHERE  timestamp >= NOW() - INTERVAL '30 days'
    GROUP  BY 1, 2
    ORDER  BY day DESC, events DESC
  `,
});

for (const row of result.rows) {
  console.log(row);
}
```

### Python

```python
from altertable_lakehouse import Client
from altertable_lakehouse.models import QueryRequest

client = Client(
    username="{{LAKEHOUSE_USERNAME}}",
    password="{{LAKEHOUSE_PASSWORD}}",
)

req = QueryRequest(statement="""
    SELECT
        date_trunc('day', timestamp) AS day,
        event,
        COUNT(*)                     AS events,
        ROUND(AVG(value), 2)         AS avg_value
    FROM   my_catalog.main.events
    WHERE  timestamp >= NOW() - INTERVAL '30 days'
    GROUP  BY 1, 2
    ORDER  BY day DESC, events DESC
""")

result = client.query_all(req)
for row in result.rows:
    print(row)
```

### Ruby

```ruby
require 'altertable/lakehouse'

client = Altertable::Lakehouse::Client.new(
  username: '{{LAKEHOUSE_USERNAME}}',
  password: '{{LAKEHOUSE_PASSWORD}}'
)

client.query_all(
  statement: <<~SQL, params: { "event" => "checkout_completed" }
    SELECT
      date_trunc('day', timestamp) AS day,
      event,
      COUNT(*)                     AS events,
      ROUND(AVG(value), 2)         AS avg_value
    FROM   my_catalog.main.events
    WHERE  timestamp >= NOW() - INTERVAL '30 days'
      AND  event = $event
    GROUP  BY 1, 2
    ORDER  BY day DESC, events DESC
  SQL
)[:rows].each do |row|
  p row
end
```

### Go

```go
import (
	"context"
	"fmt"
	"log"

	altertable "github.com/altertable-ai/altertable-lakehouse-go"
)

client, err := altertable.NewClient(altertable.Config{
	Username: "{{LAKEHOUSE_USERNAME}}",
	Password: "{{LAKEHOUSE_PASSWORD}}",
})
if err != nil {
	log.Fatal(err)
}

result, err := client.QueryAll(context.Background(), altertable.QueryRequest{
	Statement: `
		SELECT
			date_trunc('day', timestamp) AS day,
			event,
			COUNT(*)                     AS events,
			ROUND(AVG(value), 2)         AS avg_value
		FROM   my_catalog.main.events
		WHERE  timestamp >= NOW() - INTERVAL '30 days'
		GROUP  BY 1, 2
		ORDER  BY day DESC, events DESC
	`,
})
if err != nil {
	log.Fatal(err)
}

for _, row := range result.Rows {
	fmt.Println(row)
}
```

### PHP

```php
<?php

use Altertable\Lakehouse\Config\LakehouseConfig;
use Altertable\Lakehouse\LakehouseClient;
use Altertable\Lakehouse\Models\QueryRequest;

$config = LakehouseConfig::builder()
    ->withCredentials('{{LAKEHOUSE_USERNAME}}', '{{LAKEHOUSE_PASSWORD}}')
    ->build();

$client = new LakehouseClient($config);

$result = $client->queryAll(new QueryRequest(
    statement: <<<'SQL'
        SELECT
            date_trunc('day', timestamp) AS day,
            event,
            COUNT(*)                     AS events,
            ROUND(AVG(value), 2)         AS avg_value
        FROM   my_catalog.main.events
        WHERE  timestamp >= NOW() - INTERVAL '30 days'
        GROUP  BY 1, 2
        ORDER  BY day DESC, events DESC
    SQL,
));

foreach ($result->rows as $row) {
    print_r($row);
}
```

### Rust

```rust
use altertable_lakehouse::{AltertableClient, QueryRequest};

let client = AltertableClient::builder()
    .credentials("{{LAKEHOUSE_USERNAME}}", "{{LAKEHOUSE_PASSWORD}}")
    .build()?;

let result = client
    .query_all(&QueryRequest {
        statement: r#"
            SELECT
                date_trunc('day', timestamp) AS day,
                event,
                COUNT(*)                     AS events,
                ROUND(AVG(value), 2)         AS avg_value
            FROM   my_catalog.main.events
            WHERE  timestamp >= NOW() - INTERVAL '30 days'
            GROUP  BY 1, 2
            ORDER  BY day DESC, events DESC
        "#
        .into(),
        ..Default::default()
    })
    .await?;

for row in &result.rows {
    println!("{row:?}");
}
```

### Altertable CLI

```bash
# Query lakehouse data
altertable query "
  SELECT
    date_trunc('day', timestamp) AS day,
    event,
    COUNT(*)                     AS events,
    ROUND(AVG(value), 2)         AS avg_value
  FROM my_catalog.main.events
  WHERE timestamp >= NOW() - INTERVAL '30 days'
  GROUP BY 1, 2
  ORDER BY day DESC, events DESC
"
```

Use fully qualified table names and bound exploratory queries with `LIMIT`. In application code, store credentials in your runtime's secret configuration.

Use bind parameters for values supplied by users. Named parameters use `$name`; positional parameters use `$1`, `$2`, and so on. Send an object or array in `params` rather than interpolating values into SQL:

```json
{
  "statement": "SELECT * FROM my_catalog.main.users WHERE id = $id",
  "params": { "id": 123 }
}
```

## 3. Consume results

The default HTTP response is a JSONL stream containing query metadata, a column-name array, and result rows. Process it line by line rather than parsing the whole response as one JSON document. An SDK can handle the stream for you.

If your consumer expects a standard format, request CSV, row-oriented JSONL, or Parquet. The [response format reference](/docs/api-references/lakehouse.md#post-query) defines the exact envelopes and options.

## Tune execution when needed

- [Choose compute](/docs/lakehouse/compute/sizing.md) for workload sizing and session constraints.
- [Transpile SQL](/docs/lakehouse/sql-engine.md#transpile-another-sql-dialect) when porting queries from another dialect.
- [Inspect or cancel a query](/docs/api-references/lakehouse.md#get-query-query-uuid) through the query lifecycle endpoints.

## Troubleshooting

- **Unauthorized request:** use lakehouse credentials from the intended environment.
- **Missing table:** qualify the table with its catalog and schema, then check catalog permissions.
- **Unexpected response shape:** check whether you requested the default envelope or a standard output format.
- **No available Worker:** inspect compute availability and retry with backoff. See [compute sizing](/docs/lakehouse/compute/sizing.md).