Limity
Zrozumienie limitów Live Activities, Apple Push Notification service i API PushWard.
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 | 50 |
Max live (ongoing) activities per user | 5 |
| Max registered devices per user (Free) | 3 |
| Max registered devices per user (Individual / Family / Lifetime) | 10 |
| Max integration keys per user | 25 |
| Max widgets per user | 50 |
| Max verified email recipients per account | 50 |
Ended activities are automatically deleted 30 days after they last changed, and once deleted they no longer count toward this total. Setting an explicit ended_ttl on an activity deletes it sooner.
Free Tier Quotas
The Free tier includes a generous monthly allowance that resets on the 1st of each calendar month (UTC):
| Quota | Limit (Free) | Limit (Paid) |
|---|---|---|
| Push notifications | 500 / month | Effectively unlimited (5,000 / UTC day safety cap) |
| Live Activity updates | 250 / month | Unlimited |
| Widget updates | 50 / month | Unlimited |
| Emails | 500 / month | 2,500 / month |
Email is the only resource with a monthly cap on paid plans — 2,500 messages per month — because every email carries a real per-message delivery cost, so paid plans bundle a generous fixed allowance rather than an open-ended one. It resets on the 1st of the month alongside every other quota. Live Activity and widget updates are completely uncapped. Push notifications have no monthly cap either; they carry only a 5,000 per-UTC-day circuit breaker (≈150,000/month) that exists purely to contain a runaway automation loop — normal use never reaches it.
Once a quota is exhausted, further requests return 429 Too Many Requests with code: "quota.exceeded" and a reset_at timestamp indicating when the quota resets. See API errors for the full response shape.
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 | critical, 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 10 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 – 6 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. |