쿼리 API#
읽기 전용 SQL 쿼리를 제출하고 ID로 폴링한 다음 결과를 가져오세요. 기본적으로 서명된 Parquet 다운로드 URL을 반환하며, 더 편리하다면 인라인 JSON 행으로 받을 수 있습니다. 이 페이지의 모든 작업은 curl로 실행할 수 있습니다.
모든 요청은 bearer 자격 증명으로 인증합니다. 개인 액세스 토큰(pat_…)은 대시보드에서 생성하며 생성 시 한 번만 표시됩니다.
Authorization: Bearer pat_...토큰은 데이터 접근 권한을 부여하므로 서버 측에 보관하세요. 조직 소유 서비스 키(nsk_…)도 같은 방식으로 작동합니다. 인증을 참고하세요.
수명 주기#
쿼리는 비동기로 실행됩니다. SQL을 제출하면 status: "running"과 id가 포함된 query 객체를 즉시 받습니다. status가 succeeded 또는 failed가 될 때까지 1~2초마다 폴링하세요. 오래 실행되는 쿼리는 간격을 늘리세요. 폴링도 요청 빈도 제한에 포함됩니다.
쿼리 제출#
실행 중인 쿼리와 함께 202를 반환합니다. Location 헤더는 쿼리 URL을 가리킵니다.
| 본문 필드 | 타입 | |
|---|---|---|
sql | string | 필수. 읽기 전용 문 1개입니다. 아래 제한 사항을 참고하세요. |
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"}'{
"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시간 후 만료됩니다.
상태 및 결과 조회#
쿼리 상태를 반환하며, 성공하면 결과도 반환합니다.
| 쿼리 매개변수 | 타입 | |
|---|---|---|
format | string | files(기본값)는 서명된 Parquet 다운로드 URL을 반환하고, inline은 응답에 행을 포함합니다. |
limit | integer | inline 전용. 페이지당 행 수로, 기본값은 100, 최댓값은 1000입니다. |
starting_after | string | inline 전용. 이전 페이지의 next_cursor입니다. |
curl https://api.supernova.ai/queries/qr_... \
-H "Authorization: Bearer pat_..."{
"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는 모든 페이지의 합계입니다.
{
"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
}실패한 쿼리는 결과 대신 구조화된 오류를 포함합니다.
{
"object": "query",
"id": "qr_...",
"status": "failed",
"created": 1751731200,
"duration_ms": 64,
"error": { "message": "Binder Error: Table \"custmers\" does not exist" }
}결과 파일#
서명된 URL에는 별도 인증이 필요하지 않습니다. URL을 가진 사람은 만료 전까지 누구나 파일을 다운로드할 수 있으므로 데이터 자체처럼 취급하세요. URL은 1시간 동안 유효하며 폴링할 때마다 새로 발급됩니다. 현재 결과는 파일 1개지만 향후 큰 결과가 여러 파일로 나뉠 수 있으므로 files를 목록으로 처리하세요. URL은 HTTP 범위 요청을 지원하므로 Parquet 리더가 필요한 부분만 가져올 수 있습니다.
-- DuckDB, in your own environment
select * from read_parquet('https://storage.googleapis.com/...')# pandas
df = pd.read_parquet("https://storage.googleapis.com/...")파일 전용 응답은 Parquet를 스캔하지 않으므로 row_count가 null입니다. 정확한 행 수는 Parquet 메타데이터나 인라인 페이지에서 확인할 수 있습니다.
결과를 신속히 가져오세요. 쿼리와 결과는 제출 후 최소 24시간 보존됩니다.
SQL 제한 사항#
문이 2개 이상이면 쿼리를 실행 전에 HTTP 400으로 거부합니다(multiple_statements). 읽기 전용이 아닌 쓰기, DDL, 세션 키워드도 거부합니다(write_not_allowed). 단, 문자열 리터럴 안에서는 허용되므로 where type = 'DELETE'는 작동합니다. read_parquet나 read_csv 같은 파일 또는 스토리지 함수는 문자열 리터럴 안에 있더라도 거부합니다(forbidden_function). 길이가 100,000자를 초과해도 거부합니다(sql_too_long). 스토리지 URI(gs://, s3://, file://)는 쿼리 텍스트 어디에 있어도 거부됩니다. 이 URI가 담긴 열을 필터링하려면 스킴 없이 부분 문자열로 일치시키세요. 예: where path like '%bucket/x%'.
테이블 권한#
조직은 사람, 역할, 자격 증명별로 읽을 수 있는 테이블을 정할 수 있습니다. 권한을 설정한 경우, 해당 자격 증명으로 읽을 수 없는 테이블을 지정한 쿼리는 실행 전에 403과 테이블 이름을 반환하며 거부됩니다. 같은 테이블은 카탈로그에도 나타나지 않습니다.
{
"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은 코드 설명으로 연결합니다.
{
"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 | ;로 구분된 문이 2개 이상임 |
| 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 | 계정에서 진행 중인 쿼리가 너무 많음 |
요청 빈도 제한#
호출자마다 분당 최대 120회, 계정마다 모든 호출자를 합쳐 분당 최대 600회 요청할 수 있습니다. 어느 한도든 초과하면 초 단위 Retry-After 헤더와 함께 429를 반환합니다. 계정별로 동시에 진행할 수 있는 쿼리 수에도 상한이 있습니다(too_many_queries). 권장하는 1~2초 폴링 간격이면 호출자 1명이 여러 쿼리를 동시에 무리 없이 추적할 수 있습니다. 많은 쿼리를 병렬로 실행한다면 폴링 간격을 늘리세요.