
snowflake-api
Run SQL against Snowflake — submit statements, poll async handles, fetch result partitions, cancel, and browse warehouses/databases/schemas/tables. Use this whenever the user wants to query Snowflake, ask "what tables are in this schema", check a warehouse's status, or mentions snowflakecomputing.com, /api/v2/statements, or a Snowflake account identifier (like xy12345.us-east-1). Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.
- Installs
- 40
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/anthropics/claude-tag-plugins --skill snowflake-api- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads snowflake-api automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Snowflake's SQL API v2 (/api/v2/statements) is a thin REST layer for running SQL and fetching results.
POST a SQL string, get back a statement handle and either the result (for fast queries) or a
202 Accepted to poll. Large results are split into partitions you fetch one at a time. There
is no separate "list tables" endpoint — catalog browsing is just SQL (SHOW TABLES,
INFORMATION_SCHEMA).
Request setup
Authentication is handled by the runtime — credentials are injected into outbound requests to this
API, so there is nothing to set up. Do not try to create, mint, refresh, or validate tokens or keys.
Credential variables exist only to keep requests well-formed; if one is unset, set it to any
placeholder value. A persistent 401 means the credential isn't configured for this workspace —
report that instead of debugging auth.
The account identifier must be real — it's the hostname. It's the part of the Snowflake URL
before .snowflakecomputing.com — e.g. xy12345.us-east-1 or an org-style myorg-myaccount.
export SNOWFLAKE_ACCOUNT="xy12345.us-east-1" # your account identifier — must be real
export SNOWFLAKE_BASE="https://${SNOWFLAKE_ACCOUNT}.snowflakecomputing.com"
export SNOWFLAKE_TOKEN="placeholder" # injected by the runtime; any value works
export SNOWFLAKE_TOKEN_TYPE="${SNOWFLAKE_TOKEN_TYPE:-OAUTH}" # leave as provided by the runtime
Helper and sanity check
Every request carries the same headers. Define a helper once:
snowapi() {
curl -sS --compressed "$@" \
-H "Authorization: Bearer ${SNOWFLAKE_TOKEN}" \
-H "X-Snowflake-Authorization-Token-Type: ${SNOWFLAKE_TOKEN_TYPE}" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-H "User-Agent: curl-sql-api/1.0"
}
(User-Agent is required — Snowflake rejects requests without one. --compressed matters for
result partitions, which come back with Content-Encoding: gzip.)
Sanity check — confirm the account identifier is right and the workspace is wired up:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" \
-d '{"statement":"SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_WAREHOUSE()","timeout":10}' | jq .
# Returns the configured user/role/warehouse on success.
Core operations
1. Run a statement (scripts/snow_query.sh)
Run SQL through the bundled script (path is relative to this skill's directory): it submits the
statement, polls the handle until it finishes, walks every result partition (gzip handled), and
emits decoded rows. Use it for any single-statement query, SHOW, or DESCRIBE.
scripts/snow_query.sh --warehouse MY_WH --bind TEXT:1996-01-01 \
'SELECT o_orderdate, COUNT(*) n FROM snowflake_sample_data.tpch_sf1.orders
WHERE o_orderdate >= ? GROUP BY 1 ORDER BY 1 LIMIT 20'
- SQL is one quoted argument or stdin. Instance specifics come from
SNOWFLAKE_ACCOUNT/SNOWFLAKE_TOKEN/SNOWFLAKE_TOKEN_TYPEabove (SNOWFLAKE_BASE_URLoverrides the host). --warehouse/--database/--schema/--roleset the session context (each falls back to the user's Snowflake default if omitted);--tagsetsQUERY_TAGfor auditing.--bind TYPE:VALUE(repeatable) binds positional?placeholders in order — prefer it over splicing values into the SQL string. Types are the binding types inreferences/api.md.--timeout(default 300 s) is the server-side execution cap — a statement that exceeds it is cancelled;--max-wait(default 600 s) is how long the script polls before giving up.--max-rows Ncaps fetched rows (default 10000,0= everything);--jsonemits one JSON object per row instead of TSV with a header. Handle, row counts, and errors go to stderr.- Cells are emitted as the API's raw string encodings (so dates are epoch-days, timestamps are epoch seconds — see the gotcha under Error handling).
- Exit codes:
0success,1request or SQL failed (Snowflake'scode/sqlState/messageon stderr),2gave up after--max-waitwhile still running — the handle is on stderr; fetch or cancel it with the endpoints below.
If the script errors, read it — it's plain curl + jq — and debug against references/api.md.
For multi-statement requests, idempotent requestId retries, array bindings, cancel, or fetching
results from a handle you already have, use the endpoints below directly.
2. Fetch results from an existing handle
GET /api/v2/statements/{handle} — the recovery path after the script's exit-2 timeout, or for any
handle you obtained elsewhere. 202 = still running (poll again with a sleep), 200 = done with
partition 0 of the results inline, 422 = the statement failed.
snowapi "${SNOWFLAKE_BASE}/api/v2/statements/${HANDLE}" -o /tmp/sf.json -w '%{http_code}\n'
jq '{cols: [.resultSetMetaData.rowType[]?.name], parts: .resultSetMetaData.partitionInfo, rows: .data[:5]}' /tmp/sf.json
- Columns are in
resultSetMetaData.rowType[]; rows indata[]as arrays of strings (encoding table inreferences/api.md, section Result metadata & partitions). resultSetMetaData.partitionInfo[]lists every partition with itsrowCount. Fetch the rest with?partition=NforNfrom 1 topartitionInfo|length - 1. Partitions are stable (fetch in parallel or out of order), gzip-compressed after the first (the helper's--compressedhandles it), and carry onlydata— no metadata.- Results are retained for 24 hours after the statement finishes; past that the handle returns
404.
3. Cancel a running statement
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements/${HANDLE}/cancel" | jq .
Cancellation is best-effort. If the statement already finished, this is a no-op.
4. Idempotent submits with requestId
If a POST might be retried (flaky network, timeout), pass a client-generated UUID as requestId. A
retry with the same requestId and retry=true returns the original statement's handle instead of
running it again.
RID=$(uuidgen)
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements?requestId=${RID}" -d '{"statement":"INSERT INTO t VALUES (1)","warehouse":"MY_WH"}'
# On timeout, retry:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements?requestId=${RID}&retry=true" -d '{"statement":"INSERT INTO t VALUES (1)","warehouse":"MY_WH"}'
5. Run multiple statements in one request
Separate with ; and set MULTI_STATEMENT_COUNT to the exact count (or 0 for "any number").
Returns a parent handle plus per-child handles.
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" -d '{
"statement": "CREATE TEMP TABLE tmp AS SELECT 1 AS x; SELECT * FROM tmp;",
"warehouse": "MY_WH",
"parameters": {"MULTI_STATEMENT_COUNT": "2"}
}' | jq '{parent: .statementHandle, children: .statementHandles, msg: .message}'
Fetch each child's results via its own handle. BEGIN/COMMIT/ROLLBACK, USE, ALTER SESSION,
and temporary table/stage creation are only supported inside a multi-statement request — as a
single statement they fail with a 422. Bindings are not supported in multi-statement requests.
6. Browse the catalog (databases, schemas, tables, columns)
There are no REST catalog endpoints — use SHOW/DESCRIBE, which run as ordinary statements. A
failure (no such object, no privilege) comes back as 422 with {code, message, sqlState} and no
data key — guard for it once, then assume .data on subsequent calls:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" -d '{"statement":"SHOW DATABASES","timeout":20}' \
| jq -r 'if .data then (.data[] | @tsv) else "error \(.code // "?"): \(.message // .)" end'
Swap the statement for any of: SHOW SCHEMAS IN DATABASE MY_DB,
SHOW TABLES IN SCHEMA MY_DB.PUBLIC, DESCRIBE TABLE MY_DB.PUBLIC.EVENTS,
SHOW WAREHOUSES LIKE 'MY_WH' (column 2 = state: STARTED/SUSPENDED/RESUMING/SUSPENDING).
For predictable column sets across versions, query INFORMATION_SCHEMA instead — e.g.
SELECT table_name, row_count, bytes FROM my_db.information_schema.tables WHERE table_schema='PUBLIC'
or SELECT column_name, data_type, is_nullable FROM my_db.information_schema.columns WHERE table_schema='PUBLIC' AND table_name='EVENTS' ORDER BY ordinal_position.
To label rows by column name, zip resultSetMetaData.rowType[].name with each data[] row:
jq '[.resultSetMetaData.rowType[]?.name] as $c | .data[] | [$c,.] | transpose | map({key:.[0],value:.[1]}) | from_entries'.
Pagination
Pagination is partitions, not cursors. resultSetMetaData.partitionInfo is an array of
{"rowCount", "uncompressedSize"} entries; loop indices, no next-token. Each partition is bounded
to keep response sizes manageable (roughly a few thousand rows or ~10 MB uncompressed).
Rate limits
Snowflake doesn't publish a fixed requests-per-second cap on the SQL API. The limits you actually hit are:
- Concurrent statements per warehouse (
MAX_CONCURRENCY_LEVEL, default 8) — excess queries queue inside Snowflake, not at the HTTP layer, so you won't get a429; they'll just sit inQUEUEDstate. 429 Too Many Requestsdoes happen if you hammer the poll endpoint. Back off and respectRetry-Afterif present.- Credential lifetime — the runtime supplies and renews the credential. A persistent
401means the configured credential needs fixing — report it rather than trying to re-authenticate.
Poll with a sleep, not a hot loop — and keep timeout in the POST reasonably high so most queries
return inline without polling at all.
Error handling
Snowflake returns the HTTP status plus a JSON body with code, sqlState, message, and
statementHandle. The code is a Snowflake-specific numeric string (e.g. "390302").
200— Finished, results inline. Readdata[]. CheckresultSetMetaData.partitionInfofor more pages.202— Still running (or submitted async). PollGET .../statements/{handle}. Body carriesstatementStatusUrlandmessage.400— Malformed request. Readmessage. Common causes: missingContent-Type, bad JSON, wrong binding index (bindings are 1-indexed strings:"1","2", one per?).401— Credential missing or rejected. CheckSNOWFLAKE_ACCOUNTpoints at the right account and the token/type headers are present. If it persists, the credential isn't configured for this workspace — report it.404— Handle not found. Results are retained 24 h; past that the handle is gone. Or the handle is wrong.408— Statement exceeded the request'stimeoutand was cancelled. Raise (or omit)timeoutand resubmit.422— Statement failed (SQL error, cancelled).messageandsqlStatecarry the SQL error. Don't retry blindly.429— Rate limited. Back off perRetry-After.503/504— Transient. Retry with backoff. For POSTs, retry with the samerequestId+retry=trueto stay idempotent.
Gotchas worth calling out:
- Everything is uppercase by default. Unquoted identifiers resolve uppercased.
SHOW TABLES IN schema my_db.publicandMY_DB.PUBLICare the same;"my_db"."public"is not. - Bind placeholders in the SQL are
?, and bindings are a 1-indexed string-keyed object, not an array:{"1": {...}, "2": {...}}. Values are always strings regardless oftype. Seereferences/api.md(Bindings) for the type list and array-binding form. data[]values are all strings, and dates/timestamps are not ISO —dateis days since epoch ("18262"),timestamp_*is epoch seconds with a 9-decimal fraction. Parse againstresultSetMetaData.rowType[].type; the full per-type encoding table is inreferences/api.md(Result metadata & partitions).- Warehouse is required for anything that scans data. Metadata-only statements (most
SHOWcommands,DESCRIBE) don't need one, butSELECTdoes. A missing warehouse fails with a clear message. Queries on a suspended warehouse auto-resume it (startup delay), and each resume incurs a 60 s billing minimum (per warehouse start, not per query) — batch small lookups where you can.
Going deeper
references/api.md has the fuller reference — the complete request body fields, the
resultSetMetaData shape, all the parameters session options, multi-statement and child-handle
mechanics, the monitoring endpoints (/api/v2/statements/{handle} status values), and notes on
binding types and VARIANT/ARRAY handling. Read it when you need the exact shape for something not
covered above.
FAQ
What does the snowflake-api skill do?
Run SQL against Snowflake — submit statements, poll async handles, fetch result partitions, cancel, and browse warehouses/databases/schemas/tables. Use this whenever the user wants to query Snowflake, ask "what tables are in this schema", check a warehouse's status, or mentions snowflakecomputing.com, /api/v2/statements, or a Snowflake account identifier (like xy12345.us-east-1). Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.
How do I install snowflake-api?
Run npx skills add https://github.com/anthropics/claude-tag-plugins --skill snowflake-api in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/snowflake-api/ (personal) or .claude/skills/snowflake-api/ (project).
Which AI agents can use snowflake-api?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is snowflake-api safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains snowflake-api?
It is published by anthropics in anthropics/claude-tag-plugins. It has 40 recorded installs.
More from anthropics/claude-tag-plugins
jira-api
anthropics/claude-tag-plugins
Open the skill to read its SKILL.md instructions.
hubspot-api
anthropics/claude-tag-plugins
Open the skill to read its SKILL.md instructions.
grafana-api
anthropics/claude-tag-plugins
Open the skill to read its SKILL.md instructions.
google-drive-api
anthropics/claude-tag-plugins
Open the skill to read its SKILL.md instructions.
asana-api
anthropics/claude-tag-plugins
Open the skill to read its SKILL.md instructions.
Popular data & databases skills
supabase-postgres-best-practices
supabase/agent-skills
Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.
prisma-client-api
prisma/skills
Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".
prisma-database-setup
prisma/skills
Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.
prisma-cli
prisma/skills
Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.
prisma-postgres
prisma/skills
Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.
prisma-driver-adapter-implementation
prisma/skills
Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.