What you will be able to do
- Write setup-script stored procedures that call SYSTEM$CREATE_BILLING_EVENT or SYSTEM$CREATE_BILLING_EVENTS
- Apply the parameter rules for class, timestamps and base_charge
- Add billable events to a listing's usage-based plan so they match the app's code
- Test Custom Event Billing end to end from a consumer account
1.How Custom Event Billing works
Custom Event Billing lets an app charge for specific kinds of usage, such as each call to a stored procedure, on top of the standard usage-based pricing. Setting it up takes two steps, and both must be in place before anyone is charged:
1. In the application package: write stored procedures that call SYSTEM$CREATE_BILLING_EVENT or SYSTEM$CREATE_BILLING_EVENTS, and add them to the setup script.
2. On the listing: choose a usage-based pricing plan that includes billable events.
Only one implementation pattern is supported: the app calls the system function from inside one of its stored procedures. That procedure can be written in JavaScript, Python or Java. Other ways of working out the charge are not supported, such as using the output of a table or a UDF that tracks consumer activity, or telemetry logged in an event table. You also can't test the function while you build the package, because it only runs from an app installed in a consumer account.
Checkpoint 1 of 7· Check yourself
Which design for computing billable charges does Snowflake support?
Billable events must be emitted by calling the system function from a stored procedure in the application. Approaches based on tables, UDFs or event-table telemetry are not supported.
“Snowflake supports billable events that are emitted by calling the system function within a stored procedure in the application”Source: docs.snowflake.com
Checkpoint 2 of 7· Exam question
A provider publishes a Snowflake Native App with Snowpark Container Services to the Marketplace as a subscription-based listing. According to Snowflake's compute pool billing rules, when does SPCS compute usage generate a charge on the consumer's account?
Correct answer: A — Compute pool charges accrue whenever the pool is in the IDLE, ACTIVE, STOPPING, or RESIZING state, but not while STARTING or SUSPENDED
- A. Correct. Compute pools are billed per-second in the IDLE, ACTIVE, STOPPING, or RESIZING states, but incur no charge while STARTING or SUSPENDED.
- B. Incorrect, because Snowflake still bills for the IDLE state even when no container workload is actively executing, not solely during active processing.
- C. Incorrect. Billing is metered per-second based on state and instance configuration, not a flat monthly charge regardless of state.
- D. Incorrect. Compute pool usage is billed to the consumer's account as Snowflake infrastructure cost independent of whether the listing itself is subscription-based or usage-based.
Sources1
2.SYSTEM$CREATE_BILLING_EVENT and its parameters
SYSTEM$CREATE_BILLING_EVENT(
'<class>',
'<subclass>',
<start_timestamp>,
<timestamp>,
<base_charge>,
'<objects>',
'<additional_info>'
)| Parameter | Required? | Type | Rule |
|---|---|---|---|
| class | Required | STRING | Starts with a letter or underscore; at most 64 characters; must not start with SNOWFLAKE_; stored as uppercase |
| timestamp | Required | Integer | Event creation time (UTC), Unix milliseconds |
| base_charge | Required | DOUBLE | USD; greater than zero, less than 99,999.99, at most two decimal places |
| subclass | Optional | STRING | Same naming rules as class; only used by the provider |
| start_timestamp | Optional | INTEGER | Start of a time range; otherwise the same value as timestamp |
| objects | Optional | STRING | JSON array of fully qualified object names, max 4 KB |
| additional_info | Optional | STRING | JSON key-value pairs, max 4 KB |
The class is the field that matters most. It is the name the listing uses to price the event. Class names are compared case-insensitively. base_charge is the dollar amount for this event, so your code must calculate it before calling the function.
SYSTEM$CREATE_BILLING_EVENT is limited to one event per minute. To go beyond that, use SYSTEM$CREATE_BILLING_EVENTS and pass a JSON array of events with the same fields. Batching saves time, makes it less likely you'll hit call limits, and helps keep your events set up correctly. The documentation's examples wrap the call in a helper function, createBillingEvent, whose arguments must match the system function's parameter types.
Checkpoint 3 of 7· Match them up
Match each parameter to its role
Tap a term, then the definition that fits it.
class and base_charge are required and drive the charge. subclass and objects are optional context fields.
“Specifies the amount in US dollars to charge for the billable event.”Source: docs.snowflake.com
3.Billing patterns in the setup script
Every documented pattern has the same structure. The procedure does its work, counts something, multiplies the count by a price called billing_quantity to get base_charge, and emits an event with a descriptive class. The simplest version charges a flat amount for each procedure call:
//
// Send a billable event when a stored procedure is called.
//
var event_ts = Date.now();
var billing_quantity = 1.0;
var base_charge = billing_quantity;
var objects = "[ \"db_1.public.procedure_1\" ]";
var retVal = createBillingEvent("PROCEDURE_CALL", "", event_ts, event_ts, base_charge, objects, "");The other documented patterns change only what gets counted:
- ROWS_CONSUMED: the number of rows a query returns from a consumer table, times 2.5.
- ROWS_CHANGED: rows ingested by a MERGE (rows inserted plus rows updated), times 2.5.
- MONTHLY_ACTIVE_ROWS: rows inserted or updated for the first time in a calendar month, found by checking an updated_on column, times 0.02. You can adapt this pattern to count unique users or unique data load locations instead.
The event is always emitted with a start_timestamp and timestamp in Unix milliseconds, such as Date.now() in JavaScript or int(time.time() * 1000) in Python. The examples that use the shared helper add the event-emitting code in the same procedure that defines the helper.
Checkpoint 4 of 7· Check yourself
In the ROWS_CONSUMED example, billing_quantity is 2.5 and the query returns 40 rows. What base_charge is emitted?
base_charge is the row count multiplied by billing_quantity: 40 × 2.5 = 100.
“calculated base charge of 2.5 multiplied by the number of rows in the db_1.public.t1 table in the consumer account.”Source: docs.snowflake.com
Sources1
4.Configuring billable events on the listing
Emitting events doesn't earn anything by itself. The listing has to name each event and price it. You configure the app first so that you know every class and its billing_quantity. Then, in Provider Studio, open the draft listing, go to Pricing & Trial, choose Add, select Usage-based, and click + Billable Event once for each event:
- Class: must exactly match the class in your system function call.
- Event Display Name: a readable label, for example *Row Modified*.
- Billing Quantity: must match the billing_quantity variable in your code. For example, 0.01 charges $1.00 per 100 rows.
- Unit Name: the unit being charged for, for example *row*.
A listing can price up to eight billable events. You also add a Description of how the app bills consumers, and a Maximum Monthly Charge. Optionally you can add a monthly fee, a per-query charge with included queries, and a trial. You are paid only for events that you add to the listing. Any other events the app emits earn nothing.
Checkpoint 5 of 7· Check yourself
An app emits events with classes ROWS_CHANGED and PROCEDURE_CALL, but the listing only defines a billable event for ROWS_CHANGED. What happens to PROCEDURE_CALL events?
Only billable events that are added to the listing generate payment, even if the app emits other events.
“You are paid only for billable events that you add to your listing, even if additional types of events are emitted by your application.”Source: docs.snowflake.com
Sources3
5.Testing billable events before publishing
Test with a consumer account inside your own organization. First, release the new code: update the setup script, update the application package, and update its version and release directive. Next, create a private listing with Custom Event Billing as the pricing plan, share it with the consumer account, and install the app there through Snowsight. Then call the billing procedures from that consumer account.
Three details catch people out:
- A payment method is required, but usage within your own organization is not billed. - Running the procedures in the provider account that created the package returns an error. - The usage view has latency. Wait at least two days before you query it.
Checkpoint 6 of 7· Put it in order
Put the Custom Event Billing test steps in order
- 1.Update the application package, its version and release directive
- 2.Update the setup script with the billing stored procedures
- 3.Create a private listing with Custom Event Billing as the pricing plan
- 4.Query MARKETPLACE_PAID_USAGE_DAILY at least two days later
- 5.Share the listing and install the app in a consumer account
- 6.Call the stored procedures from the consumer account
Code first, then the listing, then installing and using the app as a consumer, and finally checking the delayed usage view.
“Update the version and release directive for your application package.”Source: docs.snowflake.com
Checkpoint 7 of 7· Fill the gap
Complete the consumer-side query that confirms billable events were generated.
SELECT listing_global_name,
listing_display_name,
charge_type,
charge
FROM SNOWFLAKE.DATA_SHARING_USAGE.MARKETPLACE_PAID_USAGE_DAILY
WHERE charge_type=' ? '
AND PROVIDER_ACCOUNT_NAME = <account_name>
AND PROVIDER_ORGANIZATION_NAME= <organization_name>;Custom Event Billing charges appear with charge_type MONETIZABLE_BILLING_EVENTS. SPCS_COMPUTE_POOL_SURCHARGE is the charge type for container surcharges.
Source: docs.snowflake.comSources1
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.You can calculate billable charges from telemetry the app writes to an event table.Why is that wrong?
The only supported approach is calling the billing system function from a stored procedure in the app. Methods based on event-table telemetry are not supported.
Covered in How Custom Event Billing works
2.The listing's Billing Quantity can be any price you choose, separate from the code.Why is that wrong?
The listing's Class and Billing Quantity must match the class and billing_quantity used in the application code.
Covered in Configuring billable events on the listing
3.You can verify billable events by calling the procedures in the provider account that built the package.Why is that wrong?
The system function only works from an app installed in a consumer account. Running the procedures in the provider account returns an error.
Covered in Testing billable events before publishing
Practise it for real
Emit a PROCEDURE_CALL billable event from an app and confirm it is recorded in a consumer account in your organization.
1.Add a stored procedure that defines the createBillingEvent helper and emits a PROCEDURE_CALL event with billing_quantity 1.0, then add it to the setup script.
Why: Billable events are only supported when the system function is called from a stored procedure in the app.
You should see: The setup script contains the billing procedure.
2.Update the application package, then update its version and release directive.
Why: Consumers only receive the new code through the release directive.
You should see: The release directive points to the version that includes the billing procedure.
3.Create a private listing with a usage-based plan, and add a billable event with Class PROCEDURE_CALL and Billing Quantity 1.0.
Why: The listing's class and billing quantity must match the code exactly, or you are not paid.
You should see: The draft listing shows one billable event and a Maximum Monthly Charge.
4.Share the listing with a consumer account in your organization, install the app in Snowsight, and call the procedure.
Why: The system function only works in an app installed in a consumer account.
You should see: The procedure returns without an error.
5.At least two days later, query MARKETPLACE_PAID_USAGE_DAILY in the consumer account, filtering on charge_type='MONETIZABLE_BILLING_EVENTS'.
Why: The view has latency, and this charge type identifies Custom Event Billing charges.
You should see: A row for your listing with a non-zero CHARGE. Usage within your organization is not billed.
Stuck? Get a nudge
If the call errors, check whether you ran it in the provider account rather than in the consumer account.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Select a usage-based pricing plan with billable events for the listing you use to publish your Snowflake Native App to consumers.”
↩︎ How Custom Event Billing works“This system function can only be called from a Snowflake Native App installed in a consumer account.”
↩︎ How Custom Event Billing works“You can call this system function in a stored procedure written in JavaScript, Python, or Java.”
↩︎ How Custom Event Billing works“By using batches, you save time, reduce the likelihood of exceeding call limits, and ensure your billing events are set up correctly.”
↩︎ SYSTEM$CREATE_BILLING_EVENT and its parameters“Monthly active rows are the number of rows inserted or updated for the first time within a calendar month.”
↩︎ Billing patterns in the setup script“When you test Custom Event Billing, you must set up a payment method but you will not be charged for usage within your organization.”
↩︎ Testing billable events before publishing“Due to latency in the view, run these queries at least two days after first using the Snowflake Native App.”
↩︎ Testing billable events before publishing“methods that use telemetry logged in an event table”
↩︎ Exam trap 1“If you run these SQL commands in the provider account that you used to create the application package, you see an error.”
↩︎ Exam trap 3“Snowflake supports billable events that are emitted by calling the system function within a stored procedure in the application”
↩︎ Checkpoint“calculated base charge of 2.5 multiplied by the number of rows in the db_1.public.t1 table in the consumer account.”
↩︎ Checkpoint“Update the version and release directive for your application package.”
↩︎ Checkpoint - 2.
“If you need to exceed the one event per minute frequency limitation of this system function, use SYSTEM$CREATE_BILLING_EVENTS.”
↩︎ SYSTEM$CREATE_BILLING_EVENT and its parameters“The value must be greater than zero, less than 99,999.99, and must not exceed two decimal places of precision.”
↩︎ SYSTEM$CREATE_BILLING_EVENT and its parameters“Specifies the amount in US dollars to charge for the billable event.”
↩︎ Checkpoint - 3.
“Enter a Class that exactly matches the class defined in the system function for your application.”
↩︎ Configuring billable events on the listing“You can charge for up to eight (8) billable events.”
↩︎ Configuring billable events on the listing“This value must match the billing_quantity variable used to calculate the base_charge in your application code.”
↩︎ Exam trap 2“You are paid only for billable events that you add to your listing, even if additional types of events are emitted by your application.”
↩︎ Checkpoint