Platforms like Netlify have more than 10 million developers.1 That is more than NYC’s 2020 population. Like Manhattan, there’s always something going on in systems at this scale. Every second

Metering usage at scale makes ingestion a system with real cost and risk. It is a classic event-driven architecture problem. At 10,000 events a second, writing one S3 object per event creates 864 million data-object PUTs a day. Over 30 days, that is 25.92 billion PUTs and a modeled $129,600 in request charges.2

A straightforward trade-off is to batch incoming events before further processing. Use five-second windows based on when each event is ingested, then split a window early when it reaches 10,000 events. At 10,000 events a second, the count cap creates a full batch each second. When traffic is quiet, the five-second window closes the batch.

Gross request savings by event rate

Batching reduces request cost from the first steady event per second. Whether it pays for its own engineering and compute depends on traffic and the skills available to build and operate it. This table shows the gross S3 request saving before those costs.

Events/second PUTs/month
per event
PUTs/month
batched
Cost/month
per event
Cost/month
batched
Gross saving/month
1 2.592M 518.4k $12.96 $2.59 $10.37
10 25.92M 518.4k $129.60 $2.59 $127.01
100 259.2M 518.4k $1,296.00 $2.59 $1,293.41
1,000 2.592B 518.4k $12,960.00 $2.59 $12,957.41
2,000 5.184B 518.4k $25,920.00 $2.59 $25,917.41
10,000 25.92B 2.592M $129,600.00 $12.96 $129,587.04
Steady event rate, 30 days, and one PUT per object. Below 2,000 EPS, the five-second window closes the batch; at 2,000 EPS the batch reaches 10,000 events. Above that point, the count limit closes batches and the request reduction stays at 10,000×.

An illustrative usage event in a billing ledger

I am building Netlify’s billing ledger and the team around it: an event-sourced ledger where every cent is explainable.

Usage-based products need a small record that can move from a request to a meter without losing its identity, model, token count, or time. This is what that record can look like.

syntax = "proto3";

import "google/protobuf/timestamp.proto";

message UsageEvent {
  string event_id = 1;
  string team_id = 2;
  string model = 3;
  uint64 input_tokens = 4;
  uint64 output_tokens = 5;
  bool billable = 6;
  google.protobuf.Timestamp occurred_at = 7; // UTC
}
An illustrative Protobuf contract for a usage event in a billing ledger.

The writer decodes the fields into DuckDB columns, then writes one compressed, queryable Parquet object. Parquet is an open, columnar format built for efficient analytical storage and selective retrieval. It is not a streaming transport: readers need its schema, structure, and types before they can find the column chunks they need.3 That makes it a useful shared format.

At this volume, it is more important than ever to preserve units and precision from emission through ingestion. Lose either and a quiet data error becomes a billing error, with money and customer trust at stake. AWS’s July 2026 incident was a unit-pricing failure in its estimated-billing computation system.

Customers saw false estimates in the billions. The failure was not in Parquet or an ingestion batch, but its warning still applies: validate values and units at every boundary.

The trade is small: accept up to one second of freshness delay and turn 10,000 paid S3 writes into one. DuckDB turns that one write into a durable Parquet file that downstream systems can use.

  1. Netlify, “Celebrating 10 million developers”, 20 January 2026. 

  2. S3 Standard in us-east-1 charges $0.005 per 1,000 PUT requests, checked 22 August 2026: S3 pricing and regional price list. A request is charged per object written, so payload size does not change the request count. 

  3. The Apache Parquet format specification places file metadata after the data and says readers first read it to find the column chunks they need.