Limits
Understanding the limits of Live Activities, Apple Push Notification service, and the PushWard API.
Most limits on this page are enforced by iOS and APNs, not by PushWard. PushWard works within Apple's constraints and is designed to maximize what's possible within them.
iOS Live Activity Limits
These limits are enforced by iOS on the device. PushWard cannot override them.
Concurrent Activities
| Limit | Value | Notes |
|---|---|---|
| Per app | 5 | iOS throws targetMaximumExceeded if exceeded |
| System-wide (all apps) | Undisclosed | iOS throws globalMaximumExceeded if exceeded |
PushWard's server enforces the same 5-activity limit proactively. When a new activity goes ongoing and the limit is reached, the lowest-priority activity is automatically preempted (ended via push) to make room.
Duration
| Phase | Duration | Behavior |
|---|---|---|
Active (ongoing) | 8 hours | iOS automatically ends the activity after 8 hours |
| Lock Screen after ending | Up to 4 hours | Remains visible for the user to glance at |
| Maximum total on screen | 12 hours | 8h active + 4h ended visibility |
PushWard's background processor detects activities that hit the 8-hour iOS limit and updates their state accordingly on the server.
Update Throttling
iOS throttles how often a Live Activity can be updated via push notifications. This is an iOS-level restriction, not a PushWard limit. Updates that exceed the budget are silently dropped by the device.
| Mode | Update Budget | How to Enable |
|---|---|---|
| Standard | ~15 updates/hour | Default behavior |
| Frequent Updates | Sub-minute delivery | Requires NSSupportsLiveActivitiesFrequentUpdates in Info.plist + user opt-in in Settings |
PushWard's iOS app has frequent updates enabled. The user can toggle this per-app in Settings > PushWard > Live Activities > More Frequent Updates.
Widget Update Limits
Apple does not publish a per-hour or per-day cap for apns-push-type: widgets. APNs delivers widget pushes opportunistically. The "~5 updates per hour" figure that circulates online is community-derived from the on-device timeline-reload budget — it is not an enforced limit.
WidgetKit budgets timeline reloads on the device, so pinning the same widget in multiple places does not multiply the budget — every instance shares one reload pool. The budget is also adaptive: widgets the user is actively viewing or has recently interacted with refresh more frequently, while widgets sitting idle in the background may render less often regardless of how many pushes you send.
PushWard coalesces widget pushes server-side to one per push_throttle window (default 15 seconds). This guards against runaway integration loops — it is not an Apple-imposed cap, and it can be raised or lowered per-widget when you create or update the widget. See push_throttle in the widgets API reference.
PushWard's iOS app uses Apple's silent background-push channel (apns-push-type: background) to refresh in-app state without waking the user. Apple throttles this channel to no more than two to three per hour — the app's view of your activities and widgets is brought current opportunistically when the device wakes, or instantly when you open the app. This does not affect Live Activity or widget pushes that drive Lock Screen and Home Screen surfaces.
PushWard API Limits
Rate Limiting
Rate limits are enforced per IP address.
| Endpoint | Limit |
|---|---|
Authentication (POST /auth/apple) | 10 requests/minute |
| All API endpoints | 200 requests/minute |
Demo (POST /demo/run) | 3 requests/minute |
A 429 Too Many Requests response includes a Retry-After: 60 header.
Activity Limits
| Limit | Default |
|---|---|
| Max total activities per user | 25 |
Max live (ongoing) activities per user | 5 |
| Max registered devices per user | 2 (free) · 5 (paid) |
| Max integration keys per user | 25 |
| Max widgets per user | 50 |
Priority-Based Eviction
When a new activity goes ongoing and the user already has 5 live activities, the server automatically ends the lowest-priority one. Eviction order: lowest priority value first, then oldest updated_at.
Priority is an integer from 0 to 10 (0 = lowest, 10 = highest).
Field Length Limits
| Field | Max Length |
|---|---|
slug | 128 characters |
name | 256 characters |
content.state | 256 characters |
content.subtitle | 256 characters |
content.completion_message | 512 characters |
content.icon | 128 characters |
content.url | 2,048 characters |
tap_action.url / url_action.url / secondary_url_action.url | 2,048 characters |
Action body (silent webhook payload) | 1,024 characters |
Action headers (combined keys + values) | 1,024 bytes total |
Action title / icon | 64 characters |
The slug must start with an alphanumeric character and may contain letters, numbers, hyphens, and underscores.
Tap Action Schemes & Methods
| Field | Constraint |
|---|---|
Action url scheme | Any scheme except javascript:, data:, file:, vbscript: (hard reject). http(s) URLs require a host; custom schemes (e.g. homeassistant://) are allowed. |
Action method | One of GET, POST, PUT, PATCH, DELETE, HEAD. Default GET. Only meaningful for silent webhooks. |
TTL Constraints
| Field | Range |
|---|---|
ended_ttl | 1 – 2,592,000 seconds (1s to 30 days) |
stale_ttl | 1 – 2,592,000 seconds (1s to 30 days) |
Share code expires_in | 60 – 604,800 seconds (1 min to 7 days) |
Template-Specific Validation
| Template | Field | Constraint |
|---|---|---|
| Generic, Steps | progress | 0.0 – 1.0 |
| Countdown | duration or end_date | Required. duration accepts seconds ("30") or units ("1h30m", "5m"). end_date must be a positive Unix timestamp. |
| Countdown | warning_threshold | ≥ 0 seconds |
| Steps | total_steps | ≥ 1 |
| Steps | current_step | 0 – total_steps |
| Steps | step_rows values | 1 – 10 per row |
| Alert | severity | warning or info |
| Gauge | value, min_value, max_value | All three required. min_value < max_value. value must be within range. progress is auto-calculated. |
| Gauge | unit | Max 32 characters |
| Timeline | value or values | At least one required. values max 4 series, keys max 32 characters. |
| Timeline | scale | linear or logarithmic |
| Timeline | decimals | 0 – 10 |
| Timeline | thresholds | Max 5 entries. Each: value (required), color, label (max 12 chars) |
| Timeline | history (server-managed) | Up to 300 points/series stored. Dynamically downsampled via LTTB to fit 4KB APNs payload. |
Widget Template Validation
| Template | Field | Constraint |
|---|---|---|
stat_list | stat_rows | 1 – 4 entries. Each row: label ≤ 32 chars, value ≤ 32 chars, unit ≤ 16 chars. |
value | trend | One of up, down, flat. Renders an inline arrow on the rectangular (medium) family only. Server accepts the field on gauge too but iOS doesn't render it there; progress, status, and stat_list ignore it. |