Skip to content

Limits

Understanding the limits of Live Activities, Apple Push Notification service, and the PushWard API.

Info

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

LimitValueNotes
Per app5iOS throws targetMaximumExceeded if exceeded
System-wide (all apps)UndisclosediOS 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

PhaseDurationBehavior
Active (ongoing)8 hoursiOS automatically ends the activity after 8 hours
Lock Screen after endingUp to 4 hoursRemains visible for the user to glance at
Maximum total on screen12 hours8h 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

Warning

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.

ModeUpdate BudgetHow to Enable
Standard~15 updates/hourDefault behavior
Frequent UpdatesSub-minute deliveryRequires 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

Warning

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.

EndpointLimit
Authentication (POST /auth/apple)10 requests/minute
All API endpoints200 requests/minute
Demo (POST /demo/run)3 requests/minute

A 429 Too Many Requests response includes a Retry-After: 60 header.

Activity Limits

LimitDefault
Max total activities per user25
Max live (ongoing) activities per user5
Max registered devices per user2 (free) · 5 (paid)
Max integration keys per user25
Max widgets per user50

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

FieldMax Length
slug128 characters
name256 characters
content.state256 characters
content.subtitle256 characters
content.completion_message512 characters
content.icon128 characters
content.url2,048 characters
tap_action.url / url_action.url / secondary_url_action.url2,048 characters
Action body (silent webhook payload)1,024 characters
Action headers (combined keys + values)1,024 bytes total
Action title / icon64 characters

The slug must start with an alphanumeric character and may contain letters, numbers, hyphens, and underscores.

Tap Action Schemes & Methods

FieldConstraint
Action url schemeAny scheme except javascript:, data:, file:, vbscript: (hard reject). http(s) URLs require a host; custom schemes (e.g. homeassistant://) are allowed.
Action methodOne of GET, POST, PUT, PATCH, DELETE, HEAD. Default GET. Only meaningful for silent webhooks.

TTL Constraints

FieldRange
ended_ttl1 – 2,592,000 seconds (1s to 30 days)
stale_ttl1 – 2,592,000 seconds (1s to 30 days)
Share code expires_in60 – 604,800 seconds (1 min to 7 days)

Template-Specific Validation

TemplateFieldConstraint
Generic, Stepsprogress0.0 – 1.0
Countdownduration or end_dateRequired. duration accepts seconds ("30") or units ("1h30m", "5m"). end_date must be a positive Unix timestamp.
Countdownwarning_threshold≥ 0 seconds
Stepstotal_steps≥ 1
Stepscurrent_step0 – total_steps
Stepsstep_rows values1 – 10 per row
Alertseveritywarning or info
Gaugevalue, min_value, max_valueAll three required. min_value < max_value. value must be within range. progress is auto-calculated.
GaugeunitMax 32 characters
Timelinevalue or valuesAt least one required. values max 4 series, keys max 32 characters.
Timelinescalelinear or logarithmic
Timelinedecimals0 – 10
TimelinethresholdsMax 5 entries. Each: value (required), color, label (max 12 chars)
Timelinehistory (server-managed)Up to 300 points/series stored. Dynamically downsampled via LTTB to fit 4KB APNs payload.

Widget Template Validation

TemplateFieldConstraint
stat_liststat_rows1 – 4 entries. Each row: label ≤ 32 chars, value ≤ 32 chars, unit ≤ 16 chars.
valuetrendOne 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.