# Idle Kinesis Stream, No Records Read

> Finds provisioned Kinesis streams with no records in or out for 30 days that still pay hourly per shard.

Source: https://zop.dev/integrations/aws/recommendations/idle-kinesis-stream-no-records-read

---

## A provisioned stream pays for shards, not records

In provisioned mode, [Kinesis Data Streams pricing](https://aws.amazon.com/kinesis/data-streams/pricing/)
charges for each shard at an hourly rate, with each shard providing 1 MB per second or 1,000
records per second of ingest. The shard-hours run whether producers write anything or not. A stream
created for a pipeline that was later rebuilt elsewhere keeps its shard bill indefinitely.

On-demand mode works differently: it bills per stream-hour plus data ingested and retrieved, so an
empty on-demand stream still has an hourly charge, but no shard capacity to remove.

## Checking a stream's traffic

```bash
aws kinesis list-streams \
  --query 'StreamSummaries[].[StreamName,StreamModeDetails.StreamMode,StreamStatus]'

aws kinesis describe-stream-summary --stream-name clickstream \
  --query 'StreamDescriptionSummary.[OpenShardCount,ConsumerCount,StreamModeDetails.StreamMode]'

aws cloudwatch get-metric-statistics --namespace AWS/Kinesis --metric-name IncomingRecords \
  --dimensions Name=StreamName,Value=clickstream \
  --start-time 2026-08-26T00:00:00Z --end-time 2026-09-25T00:00:00Z \
  --period 86400 --statistics Sum
```

Repeat with `GetRecords.Records`. AWS documents both
[metrics](https://docs.aws.amazon.com/streams/latest/dev/monitoring-with-cloudwatch.html) with the
`StreamName` dimension.

## Both directions must be quiet

The rule reads write activity (`IncomingRecords`) and read activity (`GetRecords.Records`)
together. Each series must be present, cover at least 30 days, and stay below 1 record on both the
average and the maximum. A stream nobody writes to but a consumer still polls, or one being written
to with no reader yet, is not flagged. The stream also needs a positive monthly price.

## Streams outside the rule

On-demand streams are skipped, because they have no shard capacity to reclaim. A stream reporting
zero shards is skipped for the same reason. When the shard count is unknown, the stream is treated
as provisioned and goes on to the metric test. A missing metric series stops the finding rather than
counting as zero. ZopNight also ships this finding switched off by default, so it may not appear
in your account.

## The saving is the shard cost

```text
saving = shard count x shard-hour rate x 730
cost after deletion = 0
```

## Retiring or shrinking the stream

1. List consumers: `aws kinesis list-stream-consumers --stream-arn <stream-arn>`, and check Lambda
   event source mappings and KCL applications.
2. If traffic may return in bursts, switch to on-demand instead:
   `aws kinesis update-stream-mode --stream-arn <stream-arn> --stream-mode-details StreamMode=ON_DEMAND`
   (allowed twice within 24 hours per stream).
3. Otherwise delete it: `aws kinesis delete-stream --stream-name clickstream --enforce-consumer-deletion`

**Warning**
Deleting a stream discards any records still inside the retention period. Without
`--enforce-consumer-deletion`, AWS rejects the delete while the stream has registered consumers,
which is a useful last check that nothing still reads it.
