문서 메뉴

쿼리 API#

읽기 전용 SQL 쿼리를 제출하고 ID로 폴링한 다음 결과를 가져오세요. 기본적으로 서명된 Parquet 다운로드 URL을 반환하며, 더 편리하다면 인라인 JSON 행으로 받을 수 있습니다. 이 페이지의 모든 작업은 curl로 실행할 수 있습니다.

모든 요청은 bearer 자격 증명으로 인증합니다. 개인 액세스 토큰(pat_…)은 대시보드에서 생성하며 생성 시 한 번만 표시됩니다.

bash
Authorization: Bearer pat_...

토큰은 데이터 접근 권한을 부여하므로 서버 측에 보관하세요. 조직 소유 서비스 키(nsk_…)도 같은 방식으로 작동합니다. 인증을 참고하세요.

수명 주기#

쿼리는 비동기로 실행됩니다. SQL을 제출하면 status: "running"id가 포함된 query 객체를 즉시 받습니다. statussucceeded 또는 failed가 될 때까지 1~2초마다 폴링하세요. 오래 실행되는 쿼리는 간격을 늘리세요. 폴링도 요청 빈도 제한에 포함됩니다.

쿼리 제출#

POST/queries

실행 중인 쿼리와 함께 202를 반환합니다. Location 헤더는 쿼리 URL을 가리킵니다.

본문 필드타입
sqlstring필수. 읽기 전용 문 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시간 후 만료됩니다.

상태 및 결과 조회#

GET/queries/{id}

쿼리 상태를 반환하며, 성공하면 결과도 반환합니다.

쿼리 매개변수타입
formatstringfiles(기본값)는 서명된 Parquet 다운로드 URL을 반환하고, inline은 응답에 행을 포함합니다.
limitintegerinline 전용. 페이지당 행 수로, 기본값은 100, 최댓값은 1000입니다.
starting_afterstringinline 전용. 이전 페이지의 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시간 동안 유효하며 폴링할 때마다 새로 발급됩니다. 현재 결과는 파일 1개지만 향후 큰 결과가 여러 파일로 나뉠 수 있으므로 files를 목록으로 처리하세요. URL은 HTTP 범위 요청을 지원하므로 Parquet 리더가 필요한 부분만 가져올 수 있습니다.

sql
-- 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_countnull입니다. 정확한 행 수는 Parquet 메타데이터나 인라인 페이지에서 확인할 수 있습니다.

결과를 신속히 가져오세요. 쿼리와 결과는 제출 후 최소 24시간 보존됩니다.

SQL 제한 사항#

문이 2개 이상이면 쿼리를 실행 전에 HTTP 400으로 거부합니다(multiple_statements). 읽기 전용이 아닌 쓰기, DDL, 세션 키워드도 거부합니다(write_not_allowed). 단, 문자열 리터럴 안에서는 허용되므로 where type = 'DELETE'는 작동합니다. read_parquetread_csv 같은 파일 또는 스토리지 함수는 문자열 리터럴 안에 있더라도 거부합니다(forbidden_function). 길이가 100,000자를 초과해도 거부합니다(sql_too_long). 스토리지 URI(gs://, s3://, file://)는 쿼리 텍스트 어디에 있어도 거부됩니다. 이 URI가 담긴 열을 필터링하려면 스킴 없이 부분 문자열로 일치시키세요. 예: where path like '%bucket/x%'.

테이블 권한#

조직은 사람, 역할, 자격 증명별로 읽을 수 있는 테이블을 정할 수 있습니다. 권한을 설정한 경우, 해당 자격 증명으로 읽을 수 없는 테이블을 지정한 쿼리는 실행 전에 403과 테이블 이름을 반환하며 거부됩니다. 같은 테이블은 카탈로그에도 나타나지 않습니다.

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_..."
  }
}

이 리소스가 반환하는 코드는 다음과 같습니다.

상태코드의미
400parameter_missing필수 매개변수(예: sql)가 없음
400write_not_allowed쿼리에 쓰기, DDL 또는 세션 키워드가 포함됨
400multiple_statements;로 구분된 문이 2개 이상임
400forbidden_function파일 또는 스토리지 함수를 참조함
400sql_too_longsql이 100,000자를 초과함
400invalid_formatformatfiles 또는 inline이 아님
400invalid_cursorstarting_after가 이전 페이지의 커서가 아님
403grant_table_denied이 자격 증명으로 읽을 수 없는 테이블을 쿼리에서 지정함
403service_key_permissions_off유효한 서비스 키이지만 조직에서 테이블 권한을 활성화하지 않음
403grant_introspection_denied제한된 자격 증명으로 허용되지 않는 테이블 목록 조회를 쿼리에서 수행함
404query_not_found이 계정에 해당 ID의 쿼리가 없음
409idempotency_key_reusedIdempotency-Key가 이미 다른 sql에 사용됨
429rate_limited요청 빈도 제한 초과. Retry-After를 따르세요.
429too_many_queries계정에서 진행 중인 쿼리가 너무 많음

요청 빈도 제한#

호출자마다 분당 최대 120회, 계정마다 모든 호출자를 합쳐 분당 최대 600회 요청할 수 있습니다. 어느 한도든 초과하면 초 단위 Retry-After 헤더와 함께 429를 반환합니다. 계정별로 동시에 진행할 수 있는 쿼리 수에도 상한이 있습니다(too_many_queries). 권장하는 1~2초 폴링 간격이면 호출자 1명이 여러 쿼리를 동시에 무리 없이 추적할 수 있습니다. 많은 쿼리를 병렬로 실행한다면 폴링 간격을 늘리세요.