Mobile API Best Practices · Designing APIs for Mobile Clients
Payload, Compression & Battery Efficiency
Fewer bytes, fewer wake-ups: compression, pagination, image sizing and batching.
Radio state machines keep the cellular radio in a high-power state for seconds after each transfer, so many small requests cost more battery than one larger request. Batch analytics and non-urgent sync, use Brotli/gzip, avoid oversized JSON (IDs instead of nested copies where clients cache), paginate with cursors, send image URLs sized for the device (via an image CDN), and use HTTP/2 or HTTP/3 to multiplex on one connection (HTTP/3/QUIC also survives network changes via connection migration).
Efficiency techniques
| Technique | Saves | Notes |
|---|---|---|
| Brotli / gzip | 60–90% of JSON bytes | Negotiate via Accept-Encoding |
| Sparse fields | Unused fields | fields= or GraphQL selection |
| Conditional GET (ETag) | Whole body on 304 | Great for config/catalogs |
| Batching non-urgent calls | Radio wake-ups | Analytics, logs, prefetch on Wi-Fi/charging |
| Image CDN variants | Megabytes per screen | w=, dpr=, format=avif/webp |
| HTTP/2 / HTTP/3 | Handshakes, head-of-line blocking | QUIC connection migration across networks |
| Protobuf / binary | Some bytes + parse CPU | Usually less impact than compression + trimming |
// Defer non-urgent uploads until unmetered + charging; the OS batches work.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val upload = OneTimeWorkRequestBuilder<AnalyticsUploadWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork("analytics-upload", ExistingWorkPolicy.KEEP, upload)
// Background-capable, discretionary transfer: the system picks a good time.
let config = URLSessionConfiguration.background(withIdentifier: "com.example.sync")
config.isDiscretionary = true // wait for Wi-Fi / power when possible
config.sessionSendsLaunchEvents = true
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
session.uploadTask(with: request, fromFile: batchFileURL).resume()
Common pitfalls
- Sending analytics events one request each.
- Full-resolution images to small screens.
- Polling every few seconds in the foreground.
Key points
- Batch and defer non-urgent work
- Compress and trim payloads
- Let the OS schedule background transfers
Further reading: Android: Persistent work (WorkManager)
Practice questions
Why can many small requests drain more battery than one large request?
Answer
The cellular radio stays in a high-power state for a tail period after each transfer; frequent small transfers keep re-waking it, while batching amortizes the wake-up.
What does HTTP/3 offer mobile clients specifically?
Answer
QUIC's connection migration lets a connection survive IP changes (Wi-Fi ↔ cellular) and avoids TCP head-of-line blocking, improving latency on lossy networks.
Open in the app to mark lessons complete, take notes and track your progress.