API zapytań#
Prześlij zapytanie SQL tylko do odczytu, odpytuj je według identyfikatora i odbierz wyniki — domyślnie jako podpisane adresy pobierania Parquet albo jako wiersze JSON w odpowiedzi, gdy jest to wygodniejsze. Wszystkie przykłady na tej stronie działają z curl.
Każde żądanie jest uwierzytelniane danymi bearer — osobistym tokenem dostępu (pat_…) utworzonym w dashboardzie i pokazanym raz podczas tworzenia:
Authorization: Bearer pat_...Przechowuj tokeny po stronie serwera; zapewniają dostęp do danych. Klucz usługi należący do organizacji (nsk_…) działa tak samo — zobacz Uwierzytelnianie.
Cykl życia#
Zapytania działają asynchronicznie. Przesyłasz SQL i natychmiast otrzymujesz obiekt query ze stanem "running" oraz id. Odpytuj go co 1–2 sekundy (zwiększając odstęp dla długich zapytań — odpytywanie obciąża limit szybkości), aż status zmieni się na succeeded lub failed.
Przesyłanie zapytania#
Zwraca 202 z działającym zapytaniem; nagłówek Location wskazuje jego adres URL.
| Pole treści | Typ | |
|---|---|---|
sql | string | Wymagane. Jedna instrukcja tylko do odczytu — ograniczenia poniżej. |
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
}Ponawianie jest bezpieczne z nagłówkiem Idempotency-Key: powtórzenie tego samego klucza zwraca pierwotny obiekt zapytania zamiast uruchamiać je ponownie i jest oznaczone nagłówkiem Idempotent-Replayed: true. Klucz jest powiązany z dokładnym sql pierwszego uruchomienia — użycie go z innym SQL zostaje odrzucone przez 409 idempotency_key_reused, więc zmieniona próba nigdy po cichu nie zwróci starego wyniku. Klucze wygasają po 24 godzinach.
Pobieranie stanu i wyników#
Zwraca stan zapytania, a po pomyślnym zakończeniu także wyniki.
| Parametr zapytania | Typ | |
|---|---|---|
format | string | files (domyślnie) zwraca podpisane adresy pobierania Parquet; inline zwraca wiersze w odpowiedzi. |
limit | integer | Tylko inline. Wiersze na stronę — domyślnie 100, maksymalnie 1000. |
starting_after | string | Tylko inline. next_cursor z poprzedniej strony. |
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 jest czasem wykonania bez czasu oczekiwania przed rozpoczęciem. Dla format=inline wiersze wracają stronicowane w odpowiedzi, a row_count jest sumą wszystkich stron:
{
"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
}Nieudane zapytanie zawiera ustrukturyzowany błąd zamiast wyników:
{
"object": "query",
"id": "qr_...",
"status": "failed",
"created": 1751731200,
"duration_ms": 64,
"error": { "message": "Binder Error: Table \"custmers\" does not exist" }
}Pliki wyników#
Podpisane adresy URL nie wymagają uwierzytelniania — każdy ich posiadacz może pobrać plik do czasu wygaśnięcia, dlatego traktuj je jak same dane. Adresy są ważne przez 1 godzinę; każde odpytanie wystawia nowe. Odczytuj files jako listę, choć obecnie wynik jest jednym plikiem — duże wyniki mogą w przyszłości zostać podzielone. Adresy obsługują żądania zakresów HTTP, dzięki czemu czytniki Parquet pobierają dane wybiórczo:
-- DuckDB, in your own environment
select * from read_parquet('https://storage.googleapis.com/...')# pandas
df = pd.read_parquet("https://storage.googleapis.com/...")Odpowiedzi tylko z plikami nie skanują Parquet, dlatego row_count ma w nich wartość null; dokładna liczba znajduje się w metadanych Parquet lub na dowolnej stronie inline.
Pobieraj wyniki szybko: zapytania i ich wyniki są przechowywane przez co najmniej 24 godziny od przesłania.
Ograniczenia SQL#
Zapytanie jest odrzucane przed wykonaniem (HTTP 400), jeśli zawiera więcej niż jedną instrukcję (multiple_statements), nie jest tylko do odczytu — słowa kluczowe zapisu, DDL i sesji są odrzucane, choć mogą występować w literałach tekstowych, więc where type = 'DELETE' działa (write_not_allowed) — wywołuje funkcję pliku lub magazynu, na przykład read_parquet albo read_csv, nawet w literale (forbidden_function), lub przekracza 100 000 znaków (sql_too_long). Identyfikatory URI magazynu (gs://, s3://, file://) są odrzucane wszędzie w tekście zapytania. Aby filtrować kolumnę zawierającą takie wartości, dopasuj podciąg bez schematu: where path like '%bucket/x%'.
Uprawnienia do tabel#
Organizacja może określić, które tabele mogą odczytywać poszczególne osoby, role i dane uwierzytelniające. Zapytanie wskazujące niedostępną tabelę jest odrzucane przed wykonaniem przez 403 z nazwą tabeli, a te same tabele są nieobecne w katalogu.
{
"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_..."
}
}Błędy#
Błędy zwracają odpowiadający stan HTTP i treść JSON. Każda odpowiedź ma nagłówek Request-Id; podaj go podczas kontaktu z pomocą techniczną. Błędy powiązane z konkretnym polem wskazują je w param, a doc_url prowadzi do objaśnienia kodu:
{
"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_..."
}
}Kody zwracane przez ten zasób:
| Stan | Kod | Znaczenie |
|---|---|---|
| 400 | parameter_missing | Brak wymaganego parametru, na przykład sql |
| 400 | write_not_allowed | Zapytanie zawiera zapis, DDL lub słowo kluczowe sesji |
| 400 | multiple_statements | Więcej niż jedna instrukcja rozdzielona ; |
| 400 | forbidden_function | Odwołanie do funkcji pliku lub magazynu |
| 400 | sql_too_long | sql przekracza 100 000 znaków |
| 400 | invalid_format | format nie jest files ani inline |
| 400 | invalid_cursor | starting_after nie jest kursorem z poprzedniej strony |
| 403 | grant_table_denied | Zapytanie wskazuje tabelę niedostępną dla tych danych uwierzytelniających |
| 403 | service_key_permissions_off | Prawidłowy klucz usługi w organizacji, która nie włączyła uprawnień do tabel |
| 403 | grant_introspection_denied | Zapytanie wyświetla tabele, czego ograniczone dane nie mogą robić |
| 404 | query_not_found | Brak zapytania o takim identyfikatorze na tym koncie |
| 409 | idempotency_key_reused | Idempotency-Key był już użyty z innym sql |
| 429 | rate_limited | Przekroczono limit szybkości żądań — przestrzegaj Retry-After |
| 429 | too_many_queries | Zbyt wiele zapytań w toku na koncie |
Limity szybkości#
Każdy wywołujący może wysłać do 120 żądań na minutę, a każde konto łącznie do 600 żądań na minutę dla wszystkich wywołujących. Po przekroczeniu żądania zwracają 429 z nagłówkiem Retry-After w sekundach. Istnieje też limit jednoczesnych zapytań w toku na konto (too_many_queries). Przy zalecanym odpytywaniu co 1–2 sekundy jeden wywołujący może wygodnie śledzić kilka zapytań jednocześnie — zwiększ odstęp przy większej liczbie równoległych zadań.