Use Maven Central
Snap-O publishes Android libraries version 3.1.1 and newer to Maven Central. Most Android projects already include mavenCentral(); add it to your dependency sources if yours does not.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}Add the Android dependency
Choose the dependency that matches your network client. Use the real interceptor in debug builds and its no-op counterpart in release builds. The no-op artifact preserves the same API while passing traffic through without starting the Snap-O server.
Add request interceptors
Add the interceptor once when the client is created. Requests made by that client can then be captured and made available to Snap-O. WebSockets require the wrapped factory shown below.
OkHttp
import com.openai.snapo.network.okhttp3.SnapOOkHttpInterceptor
val client = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()Ktor with the OkHttp engine
Attach the same interceptor through Ktor's OkHttp engine.
import com.openai.snapo.network.okhttp3.SnapOOkHttpInterceptor
val client = HttpClient(OkHttp) {
engine {
addInterceptor(SnapOOkHttpInterceptor())
}
}If your app already builds an OkHttp client, pass that client to Ktor instead.
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()
val client = HttpClient(OkHttp) {
engine {
preconfigured = okHttpClient
}
}HttpURLConnection
Open the connection through the Snap-O interceptor. Response details are captured when your app reads the response code or response stream.
import com.openai.snapo.network.httpurlconnection.SnapOHttpUrlInterceptor
val interceptor = SnapOHttpUrlInterceptor()
val connection = interceptor.open(URL("https://example.com"))
connection.connect()
connection.inputStream.use { body ->
// Read the response.
}
connection.disconnect()You can also wrap a connection created elsewhere.
val existing = URL("https://example.com")
.openConnection() as HttpURLConnection
val connection = SnapOHttpUrlInterceptor().intercept(existing)WebSockets
Create WebSockets through the wrapped factory. Regular HTTP calls can continue using the original client.
import com.openai.snapo.network.okhttp3.withSnapOInterceptor
val webSocketFactory = client.withSnapOInterceptor()
val webSocket = webSocketFactory.newWebSocket(request, listener)For Ktor, use the same preconfigured OkHttp client for HTTP traffic, wrap its WebSocket factory, and install Ktor's WebSockets plugin.
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(SnapOOkHttpInterceptor())
.build()
val client = HttpClient(OkHttp) {
engine {
preconfigured = okHttpClient
webSocketFactory = okHttpClient.withSnapOInterceptor()
}
install(WebSockets)
}Verify the connection
- Install and launch the debug build on an authorized Android device or emulator.
- Open Snap-O on macOS and select the connected device.
- Open Tools → Show App Inspector, use the toolbar inspector button, or press ⌘⌥I.
- Find the app process in the picker and click its Network icon, then trigger a request in the Android app.
- Open the request to inspect headers, bodies, timing, SSE, or WebSocket messages.
Each running app process has one picker row, with shortcuts for its available inspectors. At startup, Snap-O restores your last app and inspector when available, or selects another available app. During a session, it keeps captured requests visible through disconnects and reconnects when the selected app returns.
If the selected Android app stops, click Open app on the waiting screen when available, or launch it on your device.
Filter traffic
Right-click a request to add its host to the exclusion filter. Exclusion filters are saved across sessions. Use the settings button beside the exclusion summary to add or remove filters. You can also search requests without changing your saved filters.
Inspect responses
Use the arrow keys to move between requests. Server-Sent Events show whether the stream is pending, streaming, closed, or offline. When a response body is unavailable, Snap-O explains whether it is no longer retained or is not cached on this Mac. If loading fails while connected, click Retry.
Intercept requests
Use snapo network intercept to change a real API response or return mock data. The inspector shows the response delivered to the app while your Python handlers run separately. Follow the Network Interception guide for requirements, examples, and supported traffic.
Troubleshooting
- Confirm the device is online and authorized in
adb devices. - Confirm the installed variant includes the debug interceptor, not the no-op release artifact.
- Make sure the request uses the exact OkHttp client, Ktor engine, or HttpURLConnection wrapper you configured.
- Keep the Android app process running and select the newest process after an app restart.
- Update the Snap-O desktop app and Android dependency together if a protocol mismatch appears.
- The replay buffer retains up to five minutes, 10,000 events, or 16 MiB of total event data, whichever limit is reached first.
- Individual request and response bodies are captured up to 5 MiB by default; larger bodies are truncated.
Advanced setup
Debug builds initialize the on-device server automatically through SnapONetworkInitProvider. Release builds do not start the server unless Network Inspector is explicitly enabled. Most apps should keep the defaults and use the no-op release artifact.
Enable Network Inspector in release builds
If you intentionally include the real network dependency in a release build, add the following metadata directly to your application's <application> element. This opt-in applies only to Network Inspector. No-op release artifacts remain the recommended setup for most apps.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="snapo.network.allow_release"
android:value="true" />
</application>
</manifest>Provider configuration
The provider supports automatic initialization, main-process filtering, a mode label, and replay limits. Manifest overrides must use Android manifest-merger directives because the library already declares these metadata entries.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<provider
android:name="com.openai.snapo.network.SnapONetworkInitProvider"
android:authorities="${applicationId}.snapo-network-init"
android:exported="false">
<meta-data
android:name="snapo.auto_init"
android:value="false"
tools:replace="android:value" />
</provider>
</application>
</manifest>| Metadata key | Default | Purpose |
|---|---|---|
snapo.auto_init |
true |
Starts the inspector during app initialization. |
snapo.main_process_only |
true |
Restricts automatic initialization to the app's main process. |
snapo.mode_label |
safe |
Reports a custom mode label to Snap-O clients. |
snapo.buffer_window_ms |
300000 |
Sets the rolling replay window in milliseconds. |
snapo.max_events |
10000 |
Caps the number of events retained for replay. |
snapo.max_bytes |
16777216 |
Caps total retained event data at 16 MiB. |
Manual initialization
If automatic initialization is disabled, initialize the inspector from your application process with a custom NetworkInspectorConfig.
import com.openai.snapo.network.NetworkInspector
import com.openai.snapo.network.NetworkInspectorConfig
import kotlin.time.Duration.Companion.minutes
NetworkInspector.initialize(
application,
NetworkInspectorConfig(
bufferWindow = 10.minutes,
maxBufferedEvents = 20_000,
maxBufferedBytes = 32L * 1024 * 1024,
modeLabel = "debug",
),
)