Most Snowflake teams discover data sharing the day a partner asks for a nightly CSV drop. You build an export job, an S3 bucket, a PGP key, a re-ingest pipeline on the other side, and a support ticket queue for the days the file is late. Snowflake's sharing stack exists to delete all of that: the consumer queries your storage, live, with no copy and no pipeline.
This tutorial walks the four levels of that stack — direct shares, reader accounts, listings (private and Marketplace), and clean rooms — with the SQL for each and a decision rule for which one a given relationship needs.
The mental model
A share is a named object holding grants: "these tables, views, and functions, readable by these accounts." Nothing is copied. The consumer mounts the share as a read-only database and pays for the compute they use against it; you keep paying for storage.
Everything above a direct share is packaging:
| Mechanism | Consumer needs | Discovery | Best for |
|---|---|---|---|
| Direct share | Their own Snowflake account, same region/cloud (or replicated) | You send the account identifier | 1-to-few named partners |
| Reader account | Nothing — you create the account for them | N/A | Partners with no Snowflake |
| Private listing | Their own Snowflake account, any region | Appears in their Marketplace under "Private" | Many named consumers, cross-region |
| Marketplace listing | Any Snowflake account | Public catalog | Productising data, lead-gen, monetisation |
| Clean room | Their own account | Invite | Two parties who cannot see each other's rows |
Level 1: the direct share
Start from a dedicated share database so you never accidentally expose a base table.
USE ROLE accountadmin; -- or a role with CREATE SHARE
CREATE DATABASE IF NOT EXISTS share_out;
CREATE SCHEMA IF NOT EXISTS share_out.partner_a;
-- Expose a shaped view, never the raw table
CREATE OR REPLACE SECURE VIEW share_out.partner_a.orders_v AS
SELECT
order_id,
order_date,
region,
product_sku,
units,
net_amount
FROM analytics.public.fact_orders
WHERE partner_code = 'PARTNER_A';
CREATE SHARE partner_a_share
COMMENT = 'Order feed for Partner A, contract #4412';
GRANT USAGE ON DATABASE share_out TO SHARE partner_a_share;
GRANT USAGE ON SCHEMA share_out.partner_a TO SHARE partner_a_share;
GRANT SELECT ON VIEW share_out.partner_a.orders_v TO SHARE partner_a_share;
ALTER SHARE partner_a_share ADD ACCOUNTS = ORGNAME.PARTNER_ACCOUNT;
The consumer, in their account:
CREATE DATABASE partner_a_feed FROM SHARE PROVIDERORG.PROVIDERACCT.partner_a_share;
GRANT IMPORTED PRIVILEGES ON DATABASE partner_a_feed TO ROLE analyst;
SELECT region, SUM(net_amount) FROM partner_a_feed.partner_a.orders_v GROUP BY 1;
Three rules that save incidents later:
- Always share secure views, not tables. A
SECURE VIEWsuppresses optimizer behaviours that can leak filtered-out rows, and it gives you a stable contract you can refactor behind. - Never share
SELECT *. Name columns so a new PII column added upstream does not silently land in a partner's hands. - Version the interface.
orders_v1,orders_v2; deprecate on a schedule. Consumers build dashboards on your view names.
Multi-tenant sharing with one view
If you have fifty partners on the same fact table, do not build fifty views. Use CURRENT_ACCOUNT() in the shared view and a mapping table:
CREATE OR REPLACE SECURE VIEW share_out.multi.orders_v AS
SELECT o.*
FROM analytics.public.fact_orders o
JOIN admin.public.partner_accounts m
ON o.partner_code = m.partner_code
WHERE m.snowflake_account = CURRENT_ACCOUNT();
One share, one view, row access determined by who is asking. Add a row access policy on the base table if you want the same guarantee enforced centrally.
Level 2: reader accounts for partners without Snowflake
CREATE MANAGED ACCOUNT partner_b_reader
ADMIN_NAME = 'partner_b_admin',
ADMIN_PASSWORD = '<generated>',
TYPE = READER,
COMMENT = 'Reader account for Partner B';
SHOW MANAGED ACCOUNTS; -- returns the URL and locator to hand over
ALTER SHARE partner_b_share ADD ACCOUNTS = <reader_locator>;
The catch that surprises people: you pay for the reader account's compute. Put a resource monitor on its warehouses on day one.
CREATE RESOURCE MONITOR rm_partner_b
WITH CREDIT_QUOTA = 50
FREQUENCY = MONTHLY
START_TIMESTAMP = IMMEDIATELY
TRIGGERS ON 80 PERCENT DO NOTIFY
ON 100 PERCENT DO SUSPEND;
Reader accounts are a good on-ramp and a bad long-term home. Treat them as a 6–12 month bridge while the partner adopts Snowflake, or as the delivery mechanism for a small customer who will never adopt it.
Level 3: listings, cross-region, and auto-fulfillment
A direct share only works between accounts in the same cloud and region. A listing wraps the share in a product — title, description, sample SQL, usage terms — and, with auto-fulfillment, replicates the data to whatever region the consumer lives in.
CREATE LISTING retail_signals_private
IN DATA EXCHANGE snowflake_data_marketplace
SHARE retail_signals_share AS
$$
title: "Retail Demand Signals — Weekly"
description: "SKU-level demand and out-of-stock signals, refreshed Mondays 06:00 UTC."
terms_of_service:
type: "OFFLINE"
auto_fulfillment:
refresh_schedule: "10 MINUTE"
refresh_type: "SUB_DATABASE"
targets:
accounts: ["ORGX.ACCTY"]
$$
PUBLISH = TRUE;
Notes from real projects:
- Auto-fulfillment costs you replication compute and duplicate storage in each target region. Bill it back or price it in; it is the single most common surprise line item in a data-product P&L.
- Private listings are the right default for enterprise partner programmes. You get discovery, documentation, versioning, and usage telemetry without a public catalog entry.
- Consumer telemetry lives in
SNOWFLAKE.DATA_SHARING_USAGE—LISTING_ACCESS_HISTORY,LISTING_TELEMETRY_DAILY,LISTING_CONSUMPTION_DAILY. This is how you find out which partner actually uses the feed before the renewal conversation.
SELECT consumer_account_name,
SUM(query_count) AS queries,
COUNT(DISTINCT DATE_TRUNC('day', query_date)) AS active_days
FROM snowflake.data_sharing_usage.listing_access_history
WHERE query_date >= DATEADD('day', -30, CURRENT_DATE())
GROUP BY 1
ORDER BY queries DESC;
Monetising: paid listings
Snowflake supports paid listings with usage-based or flat-fee pricing, billed through Snowflake and paid out to you. The engineering work is identical to a private listing; the work that actually takes time is legal terms, a support SLA, and deciding your refresh contract. Do not build a paid listing until at least two private-listing consumers are querying it weekly.
Level 4: clean rooms, when neither side can see the other's rows
The classic case: a retailer and a brand want overlap analysis on customers, and neither may expose customer identifiers. A Snowflake Data Clean Room lets both parties join their data under templates that only emit aggregates above a minimum threshold.
The modern path is the Data Clean Rooms native app rather than hand-rolled row access policies. Conceptually:
- Both sides install the clean room app and create an environment.
- The provider links datasets and defines analysis templates — parameterised SQL the consumer may run but not read row-level output from.
- Policies enforce minimum aggregation (for example, no cell with fewer than 50 distinct identities) and column allow-lists.
- The consumer runs a template; only the aggregate result is returned.
-- Illustrative shape of a template body
SELECT p.audience_segment,
COUNT(DISTINCT p.identity_hash) AS overlap_identities,
SUM(c.conversions) AS conversions
FROM provider_data.identity p
JOIN consumer_data.exposure c
ON p.identity_hash = c.identity_hash
GROUP BY 1
HAVING COUNT(DISTINCT p.identity_hash) >= 50;
If you are hand-building, the primitives are: secure views + row access policies + a jobs table the consumer writes requests into and a task that executes approved templates. It works, and it is roughly ten times the maintenance of the native app. Only do it when a compliance team demands full control of the code path.
Governance you should put in before the first share goes out
- A dedicated
SHARING_ADMINrole that ownsshare_outand holdsCREATE SHARE. Nobody shares fromACCOUNTADMINad hoc. - Tag every shared object.
ALTER VIEW ... SET TAG governance.exposure = 'external'so a tag-based masking policy and your audit query both find them. - A weekly drift check. Anything granted to a share that is not in your registry is an incident:
SHOW SHARES;
SELECT "name", "to" FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
SHOW GRANTS TO SHARE partner_a_share;
- Egress reality check. Cross-region auto-fulfillment and reader-account compute are the two costs that grow silently. Both belong in a monthly review.
- Deprovisioning runbook.
ALTER SHARE partner_a_share REMOVE ACCOUNTS = ...on the day a contract ends, tied to the same checklist as SSO offboarding.
Choosing, in one paragraph
If the consumer is a Snowflake account in your region and the relationship is bilateral, use a direct share — it is one afternoon of work. If they have no Snowflake, use a reader account with a resource monitor and a sunset date. If you have more than a handful of consumers, or they live in other regions, move to private listings so you get versioning, documentation, and usage telemetry. Go Marketplace/paid only when the data is a product with an owner and a roadmap. Use a clean room when the blocker is not distribution but the fact that neither side is allowed to see the other's rows.
Checklist
- Shared objects are secure views with named columns, in a dedicated share database.
- Multi-tenant filtering uses
CURRENT_ACCOUNT()or a row access policy, not one view per partner. - Reader accounts have resource monitors from day one.
- Cross-region delivery goes through listings with auto-fulfillment, with replication cost accounted for.
- Consumption is reviewed monthly from
SNOWFLAKE.DATA_SHARING_USAGE. - A single role owns sharing; a weekly job diffs live grants against the registry.
- Every share has an owner, a contract reference, and an end-date runbook.
PowderInsights designs and builds Snowflake data sharing and data-product programmes — from a first partner share to private listings, auto-fulfillment economics, and clean room templates — with senior Snowflake architects and developers who have done it in production. Get in touch with the partner scenario you are trying to solve and we will map it to the right mechanism.