---
title: クエリ API
description: HTTPS で SQL を送信し、成功するまでポーリングして、結果を Parquet としてダウンロードするか、インライン JSON として読み取ります。
species: reference
---

# クエリ API

読み取り専用 SQL クエリを送信し、ID でポーリングして、結果を取得します。デフォルトでは署名付き Parquet ダウンロード URL として取得でき、必要に応じてインラインの JSON 行として取得できます。このページのすべての操作は `curl` で実行できます。

すべてのリクエストは、ベアラー認証情報で認証します。個人アクセストークン（`pat_…`）はダッシュボードで作成でき、作成時に一度だけ表示されます。

```bash fragment
Authorization: Bearer pat_...
```

トークンはデータへのアクセスを許可するため、サーバー側で保持してください。組織が所有するサービスキー（`nsk_…`）も同じように動作します。[認証](/docs/api/authentication)を参照してください。

## ライフサイクル

クエリは非同期で実行されます。SQL を送信すると、`status: "running"` と `id` を含む `query` オブジェクトがすぐに返されます。`status` が `succeeded` または `failed` になるまで、1～2 秒ごとにポーリングしてください。長時間のクエリでは間隔を延ばしてください。ポーリングもレート制限の対象です。

## クエリを送信

```endpoint
POST /queries
```

実行中のクエリとともに `202` を返します。`Location` ヘッダーは、そのクエリの URL を指します。

| 本文のフィールド | 型 | |
|---|---|---|
| `sql` | string | 必須。読み取り専用の文を 1 つ指定します。制限は後述します。 |

```bash
curl https://api.supernova.ai/queries \
  -H "Authorization: Bearer pat_..." \
  -H "Content-Type: application/json" \
  -d '{"sql": "select currency, count(*) as n from stripe.customers group by currency"}'
```

```json
{
  "object": "query",
  "id": "qr_...",
  "status": "running",
  "created": 1751731200,
  "sql": "select currency, count(*) as n ...",
  "livemode": true
}
```

`Idempotency-Key` ヘッダーを使うと、安全に再試行できます。同じキーを再送した場合は再実行せず、元のクエリオブジェクトを返し、`Idempotent-Replayed: true` ヘッダーでそのことを示します。キーは、最初に実行した `sql` と完全一致で紐づきます。異なる SQL での再利用は `409 idempotency_key_reused` で拒否されるため、内容を変更した再試行で、古いクエリ結果が黙って返されることはありません。キーは 24 時間後に期限切れになります。

## ステータスと結果を取得

```endpoint
GET /queries/{id}
```

クエリのステータスと、成功後にはその結果を取得します。

| クエリパラメーター | 型 | |
|---|---|---|
| `format` | string | `files`（デフォルト）は署名付き Parquet ダウンロード URL を返し、`inline` はレスポンス内に行を返します。 |
| `limit` | integer | `inline` のみ。1 ページあたりの行数。デフォルトは 100、最大は 1000。 |
| `starting_after` | string | `inline` のみ。前のページから取得した `next_cursor`。 |

```bash
curl https://api.supernova.ai/queries/qr_... \
  -H "Authorization: Bearer pat_..."
```

```json
{
  "object": "query",
  "id": "qr_...",
  "status": "succeeded",
  "created": 1751731200,
  "duration_ms": 812,
  "row_count": null,
  "files": [
    { "url": "https://storage.googleapis.com/...", "bytes": 18734, "expires": 1751734800 }
  ]
}
```

`duration_ms` は実行時間で、実行開始までの時間を含みません。`format=inline` を指定すると、レスポンス内にページ分割された行が返されます。`row_count` は全ページの合計です。

```json
{
  "object": "query",
  "id": "qr_...",
  "status": "succeeded",
  "created": 1751731200,
  "duration_ms": 812,
  "columns": ["currency", "n"],
  "row_count": 3,
  "data": [
    { "currency": "usd", "n": 1230 },
    { "currency": "eur", "n": 88 },
    { "currency": "gbp", "n": 41 }
  ],
  "has_more": false,
  "next_cursor": null
}
```

失敗したクエリには、結果の代わりに構造化エラーが含まれます。

```json
{
  "object": "query",
  "id": "qr_...",
  "status": "failed",
  "created": 1751731200,
  "duration_ms": 64,
  "error": { "message": "Binder Error: Table \"custmers\" does not exist" }
}
```

## 結果ファイル

署名付き URL に認証は不要です。URL を持つ人は、有効期限までファイルをダウンロードできるため、データそのものと同様に扱ってください。URL の有効期間は 1 時間で、ポーリングのたびに新たに発行されます。現在の結果は単一ファイルですが、将来は大きな結果が分割される可能性があるため、`files` はリストとして読み取ってください。URL は HTTP Range リクエストに対応しており、Parquet リーダーは必要な範囲を選択して取得できます。

```sql fragment
-- DuckDB, in your own environment
select * from read_parquet('https://storage.googleapis.com/...')
```

```text
# pandas
df = pd.read_parquet("https://storage.googleapis.com/...")
```

ファイルのみのレスポンスでは Parquet を走査しないため、`row_count` は `null` になります。正確な件数は、Parquet のメタデータ、または任意のインラインページに含まれます。

結果は速やかに取得してください。クエリとその結果は、送信後少なくとも 24 時間保持されます。

## SQL の制限

次の場合、クエリは実行前に拒否されます（HTTP 400）。文が複数ある場合（`multiple_statements`）、読み取り専用でない場合（`write_not_allowed`）、`read_parquet` や `read_csv` などのファイルまたはストレージ関数を、文字列リテラル内も含めて呼び出す場合（`forbidden_function`）、100,000 文字を超える場合（`sql_too_long`）です。書き込み、DDL、セッションのキーワードは拒否されますが、文字列リテラル内では許可されるため、`where type = 'DELETE'` は使用できます。ストレージ URI（`gs://`、`s3://`、`file://`）は、クエリ本文のどこにあっても拒否されます。それらを含む列を絞り込むには、スキームを除いた部分文字列で照合してください: `where path like '%bucket/x%'`。

## テーブル権限

組織は、各ユーザー、ロール、認証情報が読み取れるテーブルを決定できます。設定済みの組織では、この認証情報で読み取れないテーブルを指定したクエリは、実行前に `403` とテーブル名を返して拒否されます。同じテーブルは[カタログ](/docs/api/catalog)にも表示されません。

```json
{
  "error": {
    "type": "permission_error",
    "code": "grant_table_denied",
    "message": "Table stripe.charges is not available to the API for your organization",
    "doc_url": "https://api.supernova.ai/docs#grant_table_denied",
    "request_id": "req_..."
  }
}
```

## エラー

エラーは、対応する HTTP ステータスと JSON 本文を返します。すべてのレスポンスに `Request-Id` ヘッダーが含まれるため、サポートへのお問い合わせ時にお知らせください。特定の入力に関するエラーは、その入力を `param` に示し、`doc_url` にコードの説明へのリンクを示します。

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "write_not_allowed",
    "message": "Only read-only queries are allowed. ...",
    "doc_url": "https://api.supernova.ai/docs#write_not_allowed",
    "request_id": "req_..."
  }
}
```

このリソースが返すコード:

| ステータス | コード | 意味 |
|---|---|---|
| 400 | `parameter_missing` | 必須パラメーター（例: `sql`）がない |
| 400 | `write_not_allowed` | クエリに書き込み、DDL、またはセッションのキーワードが含まれている |
| 400 | `multiple_statements` | `;` で区切られた文が複数ある |
| 400 | `forbidden_function` | ファイルまたはストレージ関数が参照されている |
| 400 | `sql_too_long` | `sql` が 100,000 文字を超えている |
| 400 | `invalid_format` | `format` が `files` でも `inline` でもない |
| 400 | `invalid_cursor` | `starting_after` が前のページのカーソルではない |
| 403 | `grant_table_denied` | この認証情報で読み取れないテーブルがクエリに指定されている |
| 403 | `service_key_permissions_off` | テーブル権限を有効にしていない組織で、有効なサービスキーが使用された |
| 403 | `grant_introspection_denied` | 制限付きの認証情報では許可されないテーブル一覧の取得を、クエリが行った |
| 404 | `query_not_found` | このアカウントに、その ID のクエリが存在しない |
| 409 | `idempotency_key_reused` | `Idempotency-Key` が、異なる `sql` ですでに使用されている |
| 429 | `rate_limited` | リクエストのレート制限を超過。`Retry-After` に従ってください |
| 429 | `too_many_queries` | アカウントの処理中クエリが多すぎる |

## レート制限

各呼び出し元は 1 分間に最大 120 件、各アカウントは呼び出し元全体で 1 分間に最大 600 件のリクエストを送信できます。いずれかを超えると、リクエストは `429` と、秒単位の `Retry-After` ヘッダーを返します。アカウントごとの同時処理中クエリ数にも上限があります（`too_many_queries`）。推奨の 1～2 秒間隔であれば、1 つの呼び出し元で数件のクエリを同時にポーリングできます。多数を並行実行する場合は、間隔を延ばしてください。
