Karui app icon

KN001: Measuring the Background Rate Limit of Apple’s On-Device Foundation Model

Karui

How Apple’s on-device Foundation Models limit apps running in the background, and how Karui copes.

September 28, 2026 · iOS 27.0 · Measured on iPhone 17 Pro

Overview

Karui is a private iPhone notification filter built on iOS 27’s new notification trigger.

iOS 27’s “When I receive a notification” Shortcuts trigger runs an automation for each notification from the apps you choose. Karui’s Shortcut automation calls an App Intent that runs in the background. The intent builds a prompt and makes one guided-generation request with LanguageModelSession:

let decision = try await session.respond(
    to: prompt,
    generating: TriageDecision.self
)

With Karui on screen this takes about 4 seconds, but in the background the request is often refused.

Apple documents that the limit applies only in the background, but not how large it is or how it recovers. The measurements below come from one iPhone’s logs over a few days, and a month of its History.

Apple’s TN3193 covers the model’s context window. This note covers how often an app can use the model in the background in practice.

What the limit looks like

Refusals arrive as the rateLimited generation error in under 35 milliseconds, too fast for the model to have run. Apple’s documentation for the error says:

“This error will only happen if your app is running in the background and exceeds the system defined rate limit.”

Karui’s logs showed the same pattern each time, a burst of accepted calls followed by hours of refusals.

WindowAcceptedThen refused
10:37–11:16 PM8 calls11:48 PM to 5:13 AM
2:44–2:50 PMAbout 7 calls3:03 to 4:59 PM
5:54–6:32 PM8 calls7:59 to 9:49 PM

All calls were in the background, with the iPhone unplugged. Three slow calls of 8 to 15 seconds got through after midnight on the first night.

In one case, refusals continued across a 74-minute gap, so I assume this isn’t a simple hourly quota.

A month of History

Karui’s History records what made each notification’s first sort. In its export from September 20 to October 21, 2026:

The export doesn’t say whether Karui was on screen for a request, so some accepted requests were probably made in the foreground, which would make bursts look longer than they were. Karui 0.2.3 records the app’s state with each sort, so later exports can tell them apart. A stretch is measured to the next accepted request, so it also depends on when notifications arrived.

Factors that did and didn’t affect refusals

Karui logs the device’s state with every model call. Refusals happened with the iPhone locked and unlocked, at nominal thermal state, with Low Power Mode off, and at 60 to 100 percent battery.

As Apple’s documentation says, calls with Karui on screen weren’t affected.

One night on the charger, all 10 background calls between 1:30 and 5:51 AM succeeded. They were also spread out, about two an hour. The first refusal came 15 minutes after the iPhone was unplugged.

In a later log, calls failed the same way in Low Power Mode at 20 percent battery, in under 20 milliseconds.

Calls that got through took a median of about 3 seconds with Karui on screen (19 calls) and about 4 seconds in the background (57 calls). The slowest took 12.5 seconds.

Misleading symptoms

The error message

The rate-limit error message suggests using non-streaming requests in background activities. Karui never streams though.

Timeouts that are really suspensions

One call “timed out” after 70 seconds against a 20-second limit. ContinuousClock keeps counting while the process is suspended, and iOS had suspended Karui mid-call.

A later log showed the same thing at a larger scale. Two calls started with Karui on screen reported timing out after 25 and 46 minutes. Each timeout was logged in the same millisecond iOS resumed Karui, so the limit only fired once Karui could run again.

Other failures

Rarely, a call fails with an error from the safety layer’s sensitive content analysis instead.

Coping

Karui still delivers every notification when the model refuses:

  1. It falls back to hard-coded rules. A deterministic classifier decides right away. Important contacts, mentions, one-time codes, and urgent terms were already decided by rules before the model ran.
  2. It rechecks once it comes to the foreground. Notifications decided by rules because of a rate limit are marked and reclassified the next time Karui is opened.
  3. It tries BGProcessingTask. Karui schedules one after a rate limit, and iOS ran them 15 minutes to 2 hours later, even unplugged. Whether model calls from a background task get a larger budget is still unknown.
  4. It asks Private Cloud Compute instead, through the user’s shortcut. The triage intent returns a flag and a ready-made prompt, the shortcut’s Use Model action sends the prompt to Private Cloud Compute, and a second intent applies the answer. In Karui’s logs, all 8 rate-limited notifications one morning got an answer 2 to 3 seconds after the on-device refusal. For how Karui builds on this, see KN002.

Private Cloud Compute has its own per-device limit, which Karui’s former Always mode reached after about 10 requests in an hour and a quarter. KN002 covers that limit and how Karui backs off.

The limit also covers an app’s own calls to Private Cloud Compute. While Karui’s on-device model was rate limited, all 64 of its direct calls to PrivateCloudComputeLanguageModel were refused within milliseconds, but the shortcut’s Use Model step, under Shortcuts’ own budget, answered more than half. The shortcut is the way around this limit; KN002 has the measurements.

Revision History

← All Technical Notes