---
title: How to Detect Satellite Networks in Capacitor
description: Learn how to detect satellite and ultra-constrained networks in a Capacitor app on Android 15+ and iOS 26 and adapt your app to limited bandwidth.
date:
  created: 2026-08-09
  updated: 2026-08-09
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - Capacitor Network: sdks/capacitor/network.md
faq: true
---

# How to Detect Satellite Networks in Capacitor

To detect satellite networks in a Capacitor app, install the [Capacitor Network plugin](../../sdks/capacitor/network.md) and call [`getStatus()`](../../sdks/capacitor/network.md#getstatus): on Android 15+, a satellite connection is reported as the `SATELLITE` connection type, and on iOS 26+, a carrier-provided satellite connection is reported as `CELLULAR` with the `ultraConstrained` property set to `true`.

Satellite connectivity has moved from emergency-only messaging to real data connections for apps. Carriers now connect unmodified phones directly to satellites, Android 15 introduced a dedicated satellite network transport, and iOS 26 added APIs for handling ultra-constrained networks. This guide shows how to detect these connections on both platforms, how to opt your app into them, and how to adapt to bandwidth that is a fraction of what your app normally sees.

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="https://capawesome.io/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## Key Takeaways

- On Android 15+, the [Capacitor Network plugin](../../sdks/capacitor/network.md) reports satellite connections as the [`ConnectionType.Satellite`](../../sdks/capacitor/network.md#connectiontype) enum value.
- On iOS 26+, Apple does not expose satellite as a connection type; carrier-provided satellite connections appear as `CELLULAR` with `ultraConstrained` set to `true`.
- Android only routes app traffic over [constrained satellite networks](https://developer.android.com/develop/connectivity/satellite/constrained-networks){:target="_blank"} for apps that opt in via the `PROPERTY_SATELLITE_DATA_OPTIMIZED` manifest entry.
- Push notifications on Android must be sent with the `bandwidth_constrained_ok` flag to be delivered over a constrained satellite network.
- On iOS, transferring data over an ultra-constrained network requires a per-request opt-in via [`allowsUltraConstrainedNetworkAccess`](https://developer.apple.com/documentation/foundation/urlrequest/allowsultraconstrainednetworkaccess){:target="_blank"}.
- `SATELLITE` describes what the connection is; `ultraConstrained` describes how limited it is. The two don't always coincide.

## Why Satellite Networks Matter for Mobile Apps

Until recently, a phone without cell coverage was simply offline. That assumption no longer holds: carriers such as T-Mobile connect standard phones directly to Starlink satellites, and Apple has offered satellite messaging on iPhones for several years. For apps, this creates a connection class that didn't exist before, one that works in the middle of nowhere but delivers very little bandwidth at high latency.

Treating a satellite connection like Wi-Fi is a recipe for timeouts and drained batteries. Treating it like being offline wastes a connection the user may urgently need, since remote areas are exactly where messaging, navigation, and safety features matter most. The right behavior is in between, and it starts with knowing what kind of network you're on.

## How to Detect a Satellite Connection on Android

On Android 15 and later, the system exposes satellite as its own network transport, and the plugin surfaces it as a dedicated value of the [`ConnectionType`](../../sdks/capacitor/network.md#connectiontype) enum. To install the Capacitor Network plugin, please refer to the [Installation](../../sdks/capacitor/network.md#installation) section in the plugin documentation. Detection is then a single comparison:

```typescript
import { ConnectionType, Network } from '@capawesome/capacitor-network';

const isOnSatellite = async () => {
  const { connectionType } = await Network.getStatus();
  return connectionType === ConnectionType.Satellite;
};
```

Since the check runs through the regular [`getStatus()`](../../sdks/capacitor/network.md#getstatus) call, you can also catch the moment a device switches to satellite by listening for the [`networkStatusChange`](../../sdks/capacitor/network.md#addlistenernetworkstatuschange-) event, which delivers the same status object on every change.

## How to Detect Ultra-Constrained Networks on iOS

iOS takes a different approach: Apple does not expose satellite as a connection type at all. Instead, iOS 26 introduced the concept of ultra-constrained networks, connections that are severely limited in bandwidth, and reports a carrier-provided satellite connection as `CELLULAR` with that flag set. The plugin exposes it as the `ultraConstrained` property:

```typescript
import { Network } from '@capawesome/capacitor-network';

const isUltraConstrained = async () => {
  const { ultraConstrained } = await Network.getStatus();
  return ultraConstrained === true;
};
```

The strict comparison against `true` matters, because `ultraConstrained` is `null` where the platform can't determine it: on the web and on iOS versions below 26. On Android, the property is also set on satellite networks and on any network the system reports as bandwidth-constrained, which makes it the better cross-platform signal for "assume almost no bandwidth" than checking the connection type alone.

## Opting In to Constrained Satellite Networks on Android

Detecting a satellite network is only half the story on Android: by default, the system does not route your app's traffic over constrained satellite networks at all. Android reserves them for apps that declare themselves optimized for extremely limited bandwidth and variable latency, which you do with a `meta-data` entry inside the `application` element of your `AndroidManifest.xml`:

```xml
<meta-data
  android:name="android.telephony.PROPERTY_SATELLITE_DATA_OPTIMIZED"
  android:value="PACKAGE_NAME"
/>
```

Replace `PACKAGE_NAME` with your app's package name. The plugin intentionally does not add this entry for you, because each app has to decide for itself whether it behaves well on a connection this limited. Two more things to know before opting in: push notifications only arrive over a constrained satellite network when they are sent with the `bandwidth_constrained_ok` flag, and Google's [constrained satellite networks documentation](https://developer.android.com/develop/connectivity/satellite/constrained-networks){:target="_blank"} describes the full requirements.

## Allowing Data Transfers on iOS

iOS gates ultra-constrained networks per request rather than per app. Detecting the state requires no entitlement, but actually transferring data over such a network means allowing it on each request via [`allowsUltraConstrainedNetworkAccess`](https://developer.apple.com/documentation/foundation/urlrequest/allowsultraconstrainednetworkaccess){:target="_blank"}, and being allowed on all carriers may additionally require the [`com.apple.developer.networking.carrier-constrained.appcategory`](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.networking.carrier-constrained.appcategory){:target="_blank"} entitlement. The plugin neither adds entitlements nor modifies your `URLSession` configuration; Apple's guide on [configuring your app for ultra-constrained networks](https://developer.apple.com/documentation/bundleresources/configuring-your-app-for-ultra-constrained-networks){:target="_blank"} covers the setup.

## Adapting Your App to Satellite Bandwidth

Once you can detect the constraint, the adaptation itself is ordinary engineering: send less, less often. A listener keeps a single flag current, and the rest of the app consults it before doing anything heavy:

```typescript
import { Network } from '@capawesome/capacitor-network';

let deferHeavyTransfers = false;

const watchNetworkConstraints = async () => {
  await Network.addListener('networkStatusChange', status => {
    deferHeavyTransfers = status.ultraConstrained === true;
  });
};
```

With that flag in place, a few rules of thumb go a long way. Sync text before media, and queue images, videos, and large payloads until the constraint lifts. Lengthen request timeouts, since satellite latency is variable by nature. And skip anything speculative: prefetching, background refreshes, and analytics batches can all wait. If your app already distinguishes metered and data-saving connections with the `expensive` and `constrained` properties, satellite handling slots into the same decision logic; our guide on [how to detect the network status in Capacitor](./how-to-detect-the-network-status-in-a-capacitor-app.md) covers those properties in detail.

## FAQ

### What is the difference between the `SATELLITE` connection type and `ultraConstrained`?

The connection type describes what the connection is, while `ultraConstrained` describes how limited it is. They don't always coincide: a satellite network is not necessarily reported as bandwidth-constrained, and a bandwidth-constrained network is not necessarily a satellite network. `SATELLITE` is Android-only, while `ultraConstrained` is available on Android and iOS 26+.

### Why doesn't iOS report satellite as a connection type?

Apple exposes the condition as a property of the connection rather than as an interface type. A carrier-provided satellite connection on iOS therefore appears as `CELLULAR` with `ultraConstrained` set to `true`, and there is no way for any plugin to report it differently.

### Which platform versions support satellite detection?

The `SATELLITE` connection type requires Android 15 or later, and the `ultraConstrained` property requires Android or iOS 26 or later. On older iOS versions and on the web, `ultraConstrained` is `null`, so a strict `=== true` check degrades safely everywhere.

### Will my app's traffic automatically use a satellite network?

Not necessarily. On Android, constrained satellite networks only carry traffic for apps that opt in via the `PROPERTY_SATELLITE_DATA_OPTIMIZED` manifest entry. On iOS, each request must allow ultra-constrained network access explicitly. Detection through the plugin works in both cases regardless of whether you opted in.

## Try Capawesome

Satellite handling is easiest to get right before your users need it. Subscribe below to get new Capacitor guides like this one as they are published.

[Subscribe to the Capawesome Newsletter](https://capawesome.io/newsletter/){ .md-button .md-button--primary }

## Conclusion

Satellite support in a Capacitor app is two checks and two opt-ins: compare the connection type against `SATELLITE` on Android, check `ultraConstrained` on iOS 26+, declare `PROPERTY_SATELLITE_DATA_OPTIMIZED` in your Android manifest, and allow ultra-constrained access per request on iOS. Few apps handle this today, which makes it an easy way to stand out in exactly the situations where users have no alternative.

For the fundamentals this guide builds on, from reachability to metered connections, read [How to Detect the Network Status in Capacitor](./how-to-detect-the-network-status-in-a-capacitor-app.md). If you have questions, join the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}, and subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/){:target="_blank"} to stay up to date with new plugins and guides.
