npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tlj-blocks/billing-alarm-cdk

v0.1.0

Published

AWS CDK stack and construct that email you when your AWS bill crosses spend thresholds

Readme

@tlj-blocks/billing-alarm-cdk

CloudWatch alarms that email you when your AWS bill passes a spend threshold. The default thresholds are 10 USD and 100 USD.

There is one alarm per threshold, so crossing 10 USD sends an email and crossing 100 USD later in the same month sends a second one.

Install

npm install @tlj-blocks/billing-alarm-cdk

aws-cdk-lib and constructs are peer dependencies. Your CDK app already has them, but if not:

npm install aws-cdk-lib constructs

Requires aws-cdk-lib 2.160.0 or later and constructs 10.x.

Quick start

// bin/app.ts
import { App } from "aws-cdk-lib";
import { BillingAlarmStack } from "@tlj-blocks/billing-alarm-cdk";

const app = new App();

new BillingAlarmStack(app, "BillingAlarms", {
  notificationEmail: "[email protected]",
  // thresholds defaults to [10, 100]
  env: { account: process.env.CDK_DEFAULT_ACCOUNT },
});
cdk deploy BillingAlarms

You do not set a region. The stack pins itself to us-east-1, because that is the only region where AWS publishes the billing metric. See Why us-east-1.

Two steps you have to do by hand

CloudFormation cannot do either of these, and the alarms report no data until you finish the first one.

1. Turn on billing alerts

Open the billing preferences page, tick "Receive CloudWatch billing alerts", and save. Do this in the payer account if you use AWS Organizations.

Until you do, AWS never publishes the EstimatedCharges metric and every alarm sits in INSUFFICIENT_DATA. After you switch it on, the first data point can take up to 24 hours to appear.

2. Confirm the subscription email

The first deploy creates an SNS email subscription, and AWS sends a confirmation link to notificationEmail. Click it. Until you do, SNS delivers nothing.

Using the construct in a stack you already have

Use BillingAlarm instead of BillingAlarmStack if you would rather not deploy a separate stack. Put it in a us-east-1 stack yourself.

import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { BillingAlarm } from "@tlj-blocks/billing-alarm-cdk";

export class OpsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, { ...props, env: { ...props?.env, region: "us-east-1" } });

    new BillingAlarm(this, "BillingAlarm", {
      notificationEmail: "[email protected]",
      thresholds: [10, 100, 500],
      alarmNamePrefix: "personal",
    });
  }
}

Props

BillingAlarm and BillingAlarmStack both take these. BillingAlarmStack also takes the usual StackProps, except that it sets env.region itself.

| Prop | Type | Default | Notes | | ------------------- | ------------- | ----------- | --------------------------------------------------------------- | | notificationEmail | string | required | Who gets the email. Optional only if you pass topic. | | thresholds | number[] | [10, 100] | One alarm each. Must be positive and must not repeat. | | currency | string | "USD" | Must match your billing currency or no data arrives. | | topic | sns.ITopic | a new topic | Notify a topic you already have. Ignores notificationEmail. | | topicName | string | generated | Names the created topic. | | alarmNamePrefix | string | "aws" | Alarms are named prefix-billing-over-threshold-currency. | | period | Duration | 6 hours | Evaluation period. The metric updates a few times a day. |

What the construct exposes

topic is the sns.ITopic the alarms notify. Add subscribers to it for SMS or a chatbot.

alarms is the array of cloudwatch.Alarm, one per threshold, in the order you listed them.

BillingAlarmStack exposes its BillingAlarm as billingAlarm, so you can reach both of the above from the stack.

To email a second address:

import { Subscription, SubscriptionProtocol } from "aws-cdk-lib/aws-sns";

const billing = new BillingAlarm(this, "BillingAlarm", {
  notificationEmail: "[email protected]",
});

new Subscription(this, "Ops", {
  topic: billing.topic,
  protocol: SubscriptionProtocol.EMAIL,
  endpoint: "[email protected]",
});

How the alarms behave

AWS/Billing EstimatedCharges is a running total for the current month. An alarm that fires on the 12th stays in ALARM until charges reset on the 1st, then returns to OK and can fire again. You therefore get one email per threshold per month rather than one every evaluation period.

The flip side is that spend climbing from 10 USD to 99 USD sends no further email, because the 10 USD alarm is already in ALARM. Add more thresholds if you want finer steps.

The number AWS reports is an estimate. It excludes credits and refunds that get applied later, so it will not match your final invoice.

Missing data counts as notBreaching, so the first days of a month and the period before you switch on billing alerts do not raise false alarms.

Why us-east-1

AWS publishes billing metrics to us-east-1 only, whatever regions you actually spend in. An alarm on AWS/Billing created anywhere else receives no data and never fires. BillingAlarmStack sets the region for you and throws if you pass a different one, rather than deploying a stack that cannot work.

Alarms or AWS Budgets

This package uses CloudWatch alarms. They are plain CloudFormation, and the first 10 alarms in an account are free.

AWS Budgets is the better fit if you want alerts on a forecast, such as "you are on track to exceed 100 USD", or budgets split by service or tag, or alerts on usage rather than cost. Nothing stops you running both.

Resources created

An AWS::SNS::Topic, an AWS::SNS::Subscription of protocol email, and one AWS::CloudWatch::Alarm per threshold.

Troubleshooting

If the alarms stay in INSUFFICIENT_DATA, billing alerts are probably still off. Check step 1 above, then confirm the metric exists:

aws cloudwatch list-metrics --namespace AWS/Billing --region us-east-1

An empty result means AWS is not publishing it yet. Allow 24 hours after enabling the preference.

If an alarm fires but no email arrives, the SNS subscription is unconfirmed. Look in the inbox and the spam folder for the confirmation from AWS.

If real spend never trips an alarm, check that currency matches your billing currency, and that you deployed into the payer account. Member account charges in AWS Organizations roll up to the payer, not to the member.