Skip to content

Salesforce Platform Event Delivery Limits: How to Measure, Attribute and Cut Usage

Deliveries are counted per subscriber, so cost grows with consumers rather than event volume. How to measure usage, attribute it per client, and reduce it.

TL;DR

  • Deliveries are counted per subscribed client, not per published event. Every consumer you add raises the cost of every event you already publish.
  • Daily allocations are 50,000 for Performance and Unlimited, 25,000 for Enterprise, 25,000 for Professional with the API add-on, and 10,000 for Developer, on a rolling 24-hour window.
  • Pub/Sub API clients, CometD clients, empApi Lightning components and event relays all count. Apex triggers, Flows and Process Builder do not.
  • The failure is a hard one: 403::Organization total events daily limit exceeded, and by then every subscriber is affected, not just the one that caused it.
  • Measure with PlatformEventUsageMetric, and enable Enhanced Usage Metrics to get the Client field, which is how you find out which subscriber is spending your allocation.
  • The largest reduction comes from filtered custom channels, because unmatched events are never delivered and never counted. Everything in this post is generally available.

What You'll Learn

  • Why the cost model scales with consumers rather than event volume
  • Four realistic scenarios where the multiplier gets away from a team
  • How to query usage and attribute it to a specific client
  • Which object looks like it lists your subscribers but does not
  • The reduction levers in order of impact, and the one control you cannot get without a Beta feature

The Problem

Platform events are usually introduced by one team solving one problem, and the initial arithmetic looks comfortable. You publish a few thousand events a day against an allocation of 25,000, and nobody thinks about it again.

What nobody sizes is the multiplier. The allocation counts deliveries, and a delivery happens once per subscribed client. The publishing side stays flat while the consuming side grows, because subscribing is easy, requires no coordination with the publisher, and often does not even require a conversation. A partner connects. A developer adds a Lightning component. Someone enables a relay. Each of those multiplies the cost of every event you were already publishing.

The failure mode is unhelpfully abrupt. You do not get a warning at 80 percent. You get 403::Organization total events daily limit exceeded, and it hits every subscriber at once, so the symptom is "our integrations stopped" rather than "the new subscriber was a mistake".

Common questions this article answers:

  • Why did our platform event usage triple when we did not publish more events?
  • How do I find out which client is consuming the allocation?
  • What can I actually do to reduce it?

Quick Answer

Platform event delivery allocations are 50,000 events per rolling 24 hours for Performance and Unlimited, 25,000 for Enterprise, 25,000 for Professional with the API add-on, and 10,000 for Developer. Deliveries are counted separately for each subscribed client, and Pub/Sub API, CometD, empApi components and event relays all count while Apex, Flows and Process Builder do not. To see usage, query PlatformEventUsageMetric for PLATFORM_EVENTS_DELIVERED; to see which client is responsible, enable Enhanced Usage Metrics, which adds the Client, EventName, EventType, UsageType and TimeSegment fields with 15-minute, hourly and daily granularity. Do not reach for EventBusSubscriber, which lists only triggers, flows and processes and specifically excludes Pub/Sub and CometD clients. To reduce usage, in order of impact: publish through custom channels with filter expressions so only matching events are delivered and counted, consolidate multiple subscribers into one (an event relay to EventBridge is one subscriber that can fan out to many consumers downstream), review which objects have Change Data Capture enabled, and aggregate small events into fewer richer ones. If you need headroom rather than efficiency, the platform events add-on raises the daily allocation by 100,000 and moves you to a monthly usage-based model.

How the allocation is counted

The rule is one line, and everything else follows from it: the number of delivered events is counted for each subscribed client.

Edition Events delivered per 24 hours
Performance and Unlimited 50,000
Enterprise 25,000
Professional (with API add-on) 25,000
Developer 10,000

Counts against the allocation:

  • Pub/Sub API subscribers
  • CometD clients
  • empApi Lightning components
  • Event relays

Does not count:

  • Apex triggers
  • Flows
  • Process Builder processes

That second list is why the model surprises people. A design validated in a sandbox where the only subscriber is an Apex trigger consumes nothing at all. The same design in production, with external clients and Lightning components attached, is metered on every one of them.

How the multiplication actually happens

The arithmetic is obvious once written down and almost never written down. Four scenarios, all of them ordinary.

Scenario 1: growth by accretion

An Enterprise org, 25,000 daily allocation. Order_Event__e publishes 3,000 events a day and that number does not change at any point in this story.

Month What was added Subscribers Daily deliveries Allocation used
0 Middleware sync 1 3,000 12%
4 Partner order feed 2 6,000 24%
9 Event relay for the data team 3 9,000 36%
14 Observability tool 4 12,000 48%
18 Second partner 5 15,000 60%

Eighteen months in, you are at 60 percent of your allocation and nobody has published a single additional event. Each of those five decisions was individually reasonable and none of them was reviewed against the allocation, because subscribing does not require the publisher's involvement.

Then the busy season arrives and order volume doubles. Publishing goes to 6,000 a day, which sounds survivable, but deliveries go to 30,000 and you are over the limit. The multiplier means a 2x traffic increase is not a 2x problem, it is a 2x problem multiplied by however many consumers you accumulated while nobody was counting.

Scenario 2: the Lightning component that quietly costs more than every integration combined

Same org, same 3,000 events a day. The service team wants live order updates on the console, so a developer adds a component using empApi. It works beautifully in testing, where one person has one tab open.

In production there are 40 agents, and agents keep tabs open. Three tabs each is conservative for a console user.

40 agents  x  3 tabs  =  120 subscribing clients
3,000 events  x  120  =  360,000 deliveries per day

Against a 25,000 allocation, the org is exhausted before mid-morning. Nothing was integrated, no partner was onboarded, and no architectural review was triggered, because from the team's perspective a component was added to a page layout.

This is the single most common way orgs blow the allocation, and it is worth understanding in detail because the fix is a client-side coordination pattern rather than a limit increase. We covered it at length in How we reduced platform event delivery costs by 60%.

Scenario 3: the Change Data Capture switch

An admin enables Change Data Capture on Account to evaluate whether it would be useful. Change Data Capture carries its own delivery allocations, documented separately from platform events, but the multiplication behaves identically.

Account has a nightly data load that touches 50,000 records. That publishes 50,000 change events. With three subscribers on the channel, that is 150,000 deliveries in a single overnight window, from one checkbox, for an evaluation nobody completed.

The compounding detail is timing. It happens at 2am, exhausts the allocation before anyone is awake, and the first symptom is unrelated integrations failing during the morning. The cause looks nothing like the effect.

Scenario 4: the replay storm

The event bus retains events for 72 hours. At 3,000 events a day that is roughly 9,000 events sitting in the bus at any moment.

A developer points a local Pub/Sub client at production on Friday to debug something, using EARLIEST because that guarantees seeing the event they are chasing. The laptop sleeps and wakes over the weekend, and the client reconnects from EARLIEST each time.

9,000 events re-consumed  x  3 reconnections  =  27,000 deliveries

That is a full Enterprise allocation spent on a laptop over a weekend, on events every production subscriber had already received correctly. Discovered Monday.

The same pattern happens in production whenever a client loses its replay position and its error handling falls back to earliest, which many clients do because it is the option that loses no data. It is the correct instinct applied without knowledge of your allocation.

Step 1: measure and attribute

You cannot manage a multiplier you cannot see, and the ordinary Setup screens will not show you this.

Query PlatformEventUsageMetric, available from API v50.0. It exposes four metrics:

  • PLATFORM_EVENTS_PUBLISHED
  • PLATFORM_EVENTS_DELIVERED
  • CHANGE_EVENTS_PUBLISHED
  • CHANGE_EVENTS_DELIVERED
SELECT Name, StartDate, EndDate, Value
FROM PlatformEventUsageMetric
WHERE Name = 'PLATFORM_EVENTS_DELIVERED'
  AND StartDate = 2026-08-01T00:00:00.000Z
  AND EndDate = 2026-08-02T00:00:00.000Z

Data updates hourly and is retained for at least 45 days, which is enough to establish a trend and to see the shape of your week. Times are UTC, so convert before you query and before you panic about a spike that turns out to be a timezone. One implementation note: avoid using this object with a batch Apex QueryLocator, and use an iterable instead.

That gets you the total. The question you actually need answered is which subscriber, and for that you enable Enhanced Usage Metrics, available from API v58.0. It adds these fields:

Field What it gives you
Client Which subscriber consumed the deliveries
EventName Which event is expensive
EventType Platform event or change event
UsageType Publishing or delivery
TimeSegment 15-minute, hourly or daily granularity

Client is the important one. It converts "we are at 78 percent of our allocation" into "the partner feed is 60 percent of our allocation", which is the difference between a worry and a decision. The 15-minute granularity is what lets you catch a replay storm as a spike rather than inferring it from a daily total.

One trap worth naming. EventBusSubscriber looks like exactly the object for this and is not. It represents triggers, processes and flows subscribed to an event, and explicitly does not include CometD or Pub/Sub API subscribers. The subscribers costing you money are precisely the ones it omits. If you have gone looking for a list of your external subscribers and come up empty, this is why: there isn't one, and Enhanced Usage Metrics attribution is the closest thing available.

Step 2: filter what gets delivered

This is the largest lever and the only one that reduces the counted number rather than the number of consumers.

Create a custom channel, add your event as a channel member, and define a filter expression on event fields. Subscribers to that channel receive only matching events, and the delivery usage counted against your allocation is reduced to only those matching events. Filtering happens before delivery, so what does not match costs nothing.

Back to scenario 1. That partner order feed subscribes to every order, 3,000 a day, but only processes orders in its own region, which is 15 percent of volume:

Unfiltered:  3,000 deliveries/day
Filtered:      450 deliveries/day

That is 2,550 daily deliveries removed by configuration, with the partner changing nothing except which channel they connect to. Do that for two or three of your noisiest consumers and the accretion in scenario 1 reverses.

Stream filtering is supported for custom platform events and change data capture events, for Pub/Sub API and CometD clients, which covers the subscribers that count against your allocation.

Step 3: cut the number of subscribers

Every subscriber is a multiplier, so removing one is worth as much as halving your publishing volume when you only had two.

Consolidate deliberately. Two teams independently subscribing to the same channel for overlapping reasons is common, and it doubles that channel's cost for no benefit. One subscriber that fans out internally is cheaper than two that each pay full price.

Use an event relay as a fan-out point. A relay to Amazon EventBridge does count against your allocation, the same as any other subscriber, so it is not free. What makes it valuable is that it counts as one. Five AWS-side consumers subscribing to Salesforce directly cost 5x per event. One relay feeding five EventBridge targets costs 1x, because the fan-out happens in AWS where Salesforce is not metering it. If your consuming architecture is already on AWS, this is the single biggest structural saving available.

Fix the Lightning side with coordination, not with a bigger limit. Scenario 2 is not solved by buying allocation. It is solved by electing one tab per user to hold the subscription.

Step 4: publish less

Audit Change Data Capture entity selection. Every object you enable publishes change events for every record change, whether or not anything useful consumes them. This is the cheapest audit on the list because it is a single Setup screen, and evaluation leftovers are common.

Aggregate. Ten small events describing one business occurrence cost ten times what one richer event costs, multiplied by every subscriber. Where consumers only act on the completed occurrence, publish once at the end rather than at each step.

Check what is publishing at all. A test harness left scheduled, a retry loop republishing on failure, or an integration echoing events back are all worth ruling out before optimising anything legitimate.

Step 5: make replay hygiene a written standard

Scenario 4 is the expensive one you cannot fix technically without the Beta managed subscriptions feature, because the replay position lives in the subscriber. What you can do is make it a condition of connecting.

Pub/Sub API clients choose a ReplayPreset of LATEST, EARLIEST or CUSTOM. Put two requirements in your integration standard and check them at onboarding:

  1. Persist the replay ID durably, committed together with the processing of the event rather than held in memory or written on a best-effort basis.
  2. Recover with LATEST, never a blind EARLIEST. If a client cannot resume from its stored position, resuming at the head of the stream and repairing the gap from your own system of record is almost always cheaper than re-reading three days of history.

Add a third rule for your own team: no client pointed at production from a laptop. That single line would have prevented scenario 4 entirely.

Step 6: raise the ceiling, if the arithmetic still does not work

The platform events add-on raises the daily allocation by 100,000 events and moves you from strict daily enforcement to a monthly usage-based model. The second half matters more than the first, because it removes the cliff edge: a bad day borrows against the month instead of taking your integrations down at 10am.

Do the reduction work first, though. Buying headroom for traffic you did not need to deliver means paying for the multiplier permanently.

Alerting, and the one control you cannot get

Salesforce offers Proactive Alert Monitoring for the daily platform event delivery limit, but it is part of the Signature Success plan. If you are not on Signature, you build it yourself, and it is not much work: a scheduled job querying PlatformEventUsageMetric for PLATFORM_EVENTS_DELIVERED over the trailing window, compared against your edition's allocation, alerting at 60 and 80 percent. Alert on the trend, because by the time you see 403::Organization total events daily limit exceeded the decision has been made for you.

The gap worth knowing about is the emergency stop. When a single subscriber is consuming your allocation right now, the generally available options are revoking that integration user's OAuth tokens or disabling the connected app. Both work and both are blunt: they remove that partner's entire access rather than just the subscription, and recovery means re-authorising rather than flipping a switch back.

A ManagedEventSubscription has a state field that takes RUN or STOP, which is exactly the surgical control you want, and it is still Beta. We make the case for adopting it anyway, and name the workloads to keep off it, in Why managed event subscriptions should be how external systems subscribe to your Salesforce events.

What to tell your integration teams

The multiplier is invisible to the people creating it, so the useful intervention is telling them it exists before they add the next subscriber:

Salesforce meters platform events by delivery, counted once for every subscribed client, not once per event published. That means adding a new subscriber raises the running cost of every event we already publish, and it comes out of one shared daily allowance for the whole org.

Before you connect anything new to our event stream, talk to us. In most cases we will point you at a filtered channel carrying only the records you actually process, which costs a fraction of the full stream and takes us very little time to set up.

Two things to avoid. Do not subscribe from a local development machine against production. And if your client loses its position in the stream, do not have it restart from the earliest available event, because that re-reads up to three days of history and every one of those counts again.

Send that to anyone who can write a subscriber, which usually includes more teams than you expect, and put it in the onboarding pack for new integrations rather than relying on it being remembered.

Frequently Asked Questions

Q: Our publishing volume has not changed, so why has usage gone up?

A: Because the allocation counts deliveries, not publications, and a delivery happens once per subscribed client. Adding a fifth subscriber to an event you already publish raises that event's cost by 25 percent without a line of publishing code changing. Query PlatformEventUsageMetric with Enhanced Usage Metrics enabled and group by the Client field to see which subscriber arrived.

Q: How do I get a list of everything subscribed to my platform events?

A: There is no single list. EventBusSubscriber covers triggers, processes and flows only and explicitly excludes CometD and Pub/Sub API clients, which are the ones that count against your allocation. The practical substitute is the Client field on PlatformEventUsageMetric with Enhanced Usage Metrics turned on, which attributes usage per client even though it is not a registry.

Q: Do Apex triggers and Flows count against the delivery allocation?

A: No. Apex triggers, Flows and Process Builder processes subscribe inside the platform and do not count. Pub/Sub API clients, CometD clients, empApi Lightning components and event relays all do. This is why a design that looked free in a sandbox full of Apex subscribers gets expensive in production.

Q: Does an event relay to EventBridge avoid the allocation?

A: No, a relay counts as a subscriber like any other. Its value is that it counts as one subscriber regardless of how many consumers sit behind it in AWS, so it converts an N-times multiplier into a 1-times multiplier for everything downstream.

Q: Will filtering actually reduce the counted usage, or just what my client sees?

A: It reduces the counted usage. Filtering is applied at the channel before delivery, so events that do not match are never delivered and never counted. This is what separates it from filtering inside your own client, which pays full price and then discards.

Q: What is the fastest thing to do when we are about to hit the limit today?

A: Identify the heaviest client from Enhanced Usage Metrics, then stop it. Without managed subscriptions, that means revoking that integration user's tokens or disabling its connected app, which is blunt but immediate. Afterwards, move that consumer to a filtered channel so the same afternoon does not repeat.

Key Takeaways

  • Cost scales with consumers, not volume. Every subscriber you add raises the price of every event you already publish.
  • The multiplication is undramatic until it is fatal. Five reasonable decisions over eighteen months took scenario 1 to 60 percent of allocation with flat publishing.
  • A Lightning component can cost more than every integration combined, because empApi counts per tab and console users keep tabs open.
  • Enhanced Usage Metrics and the Client field are how you attribute usage. EventBusSubscriber will not show you external subscribers and is not the tool for this.
  • Filtered custom channels are the largest reduction lever, because unmatched events are never delivered and never counted.
  • Everything here is generally available. The only thing you cannot get without the Beta feature is a surgical stop switch.

What's Next?

Recommended Reading:

Action Items:

  1. Enable Enhanced Usage Metrics, then query PlatformEventUsageMetric grouped by Client to find out which subscribers are actually spending your allocation.
  2. Take your two heaviest consumers and move them to custom channels with filter expressions scoped to what they genuinely process.
  3. Schedule an alert at 60 and 80 percent of your edition's allocation, and audit which objects have Change Data Capture enabled while you are there.

Resources & References