9  REST API Design & Practice

In 60 Seconds

This chapter provides hands-on practice designing RESTful IoT APIs through worked examples covering resource hierarchies, proper HTTP status codes for device states, offline device handling, protocol selection between CoAP/MQTT/HTTP, and calculating protocol overhead impact on battery life.

9.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Design RESTful IoT APIs: Construct resource hierarchies that apply REST constraints to multi-device deployments
  • Implement Proper Error Handling: Select and apply the correct HTTP status codes for all device states including offline and missing resources
  • Diagnose Offline Device Scenarios: Distinguish between “no data” (404), “stale data” (200 with metadata), and “service unavailable” (503) in IoT REST APIs
  • Compare Protocol Trade-offs: Evaluate CoAP, MQTT, and HTTP against deployment constraints such as battery life, latency, and network topology
  • Calculate Protocol Overhead: Assess the byte-level impact of protocol choice on battery life and justify the selection for constrained IoT devices
  • Core Concept: Fundamental principle underlying REST API Design & Practice — understanding this enables all downstream design decisions
  • Key Metric: Primary quantitative measure for evaluating REST API Design & Practice performance in real deployments
  • Trade-off: Central tension in REST API Design & Practice design — optimizing one parameter typically degrades another
  • Protocol/Algorithm: Standard approach or algorithm most commonly used in REST API Design & Practice implementations
  • Deployment Consideration: Practical factor that must be addressed when deploying REST API Design & Practice in production
  • Common Pattern: Recurring design pattern in REST API Design & Practice that solves the most frequent implementation challenges
  • Performance Benchmark: Reference values for REST API Design & Practice performance metrics that indicate healthy vs. problematic operation

9.2 For Beginners: REST API Examples

This chapter walks through complete REST API designs for real IoT scenarios. You will see how to create endpoints for reading sensor data, controlling actuators, and managing device fleets. Each example includes the URL structure, request format, and response format, so you can use them as templates for your own projects.

“Let’s build an actual API for our smart garden!” said Max the Microcontroller, rolling up his sleeves. “Sammy, you’re the temperature sensor at /api/v1/sensors/garden-temp. When someone sends a GET request to your URL, you reply with your current reading in JSON.”

Sammy practiced: “GET /api/v1/sensors/garden-temp and I respond with {\"value\": 24.5, \"unit\": \"celsius\", \"timestamp\": \"2026-02-07T10:30:00Z\"}. Easy!”

“Now for the sprinkler control,” Lila the LED continued. “POST to /api/v1/actuators/sprinkler/commands with a body like {\"action\": \"water\", \"duration_minutes\": 15}. The sprinkler turns on and returns a 202 Accepted status – meaning ‘got it, working on it’ – because watering takes time.”

Bella the Battery added the fleet management angle: “And GET /api/v1/devices?status=low-battery returns all devices that need charging. See how the URL pattern is consistent? Resources are nouns, actions use HTTP verbs, and filters go in query parameters. Follow the pattern and every developer who sees your API will instantly understand it!”

9.3 Prerequisites

Before diving into this chapter, you should be familiar with:

9.4 How This Chapter Fits

REST API Design Series Navigation:

  1. Introduction and Why Lightweight Protocols Matter
  2. Protocol Overview and Comparison
  3. REST API Design for IoT (Index)
  4. Real-time Protocols
  5. Protocol Selection Worked Examples

This chapter provides hands-on practice with REST API design through worked examples and comprehensive quizzes.


9.5 Worked Examples: REST API Design for IoT

These worked examples demonstrate practical REST API design decisions for real-world IoT scenarios.

Worked Example: Designing a Smart Thermostat REST API

Scenario: You are building a REST API for a smart thermostat system that allows mobile apps to read current temperature, set target temperature, and retrieve historical data. The system has 500 deployed thermostats.

Given:

  • Thermostat reports temperature every 30 seconds
  • Users want real-time display and control from mobile app
  • Historical data retention: 30 days
  • Expected concurrent mobile app users: 2,000

Steps:

  1. Define resource hierarchy - Organize resources around the device:

    /api/v1/thermostats/{device_id}              # Device info
    /api/v1/thermostats/{device_id}/temperature  # Current reading
    /api/v1/thermostats/{device_id}/setpoint     # Target temperature
    /api/v1/thermostats/{device_id}/history      # Historical readings
  2. Choose HTTP methods for each operation:

    • GET /thermostats/thermo-42/temperature - Read current temp (idempotent)
    • PUT /thermostats/thermo-42/setpoint - Set target (full update, idempotent)
    • GET /thermostats/thermo-42/history?start=2025-01-01&end=2025-01-15 - Query with filters
  3. Design response format with proper status codes:

    // GET /thermostats/thermo-42/temperature
    // Response: 200 OK
    {
      "device_id": "thermo-42",
      "current_temp_c": 22.5,
      "humidity_pct": 45,
      "timestamp": "2025-01-15T10:30:00Z",
      "unit": "celsius"
    }
    
    // PUT /thermostats/thermo-42/setpoint with body {"target_c": 21.0}
    // Response: 200 OK (or 204 No Content)
    {
      "device_id": "thermo-42",
      "target_c": 21.0,
      "estimated_time_minutes": 15
    }
    
    // GET /thermostats/nonexistent/temperature
    // Response: 404 Not Found
    {
      "error": "DEVICE_NOT_FOUND",
      "message": "Device 'nonexistent' is not registered",
      "timestamp": "2025-01-15T10:30:00Z"
    }

Result: A clean, RESTful API with predictable endpoints, proper HTTP semantics, and clear error handling. The hierarchy /thermostats/{id}/resource scales to thousands of devices while remaining intuitive for developers.

For a deployment of 500 thermostats reporting every 30 seconds, calculate the API request load:

\[\text{Requests per second} = \frac{500 \text{ devices}}{30 \text{ s}} \approx 16.67 \text{ req/s}\]

Each GET request (with HTTP/1.1 keep-alive) averages 250 bytes overhead + 120 bytes JSON payload = 370 bytes. Daily bandwidth:

\[\text{Daily data} = 16.67 \frac{\text{req}}{\text{s}} \times 370 \frac{\text{bytes}}{\text{req}} \times 86400 \frac{\text{s}}{\text{day}} = 533 \text{ MB/day}\]

For historical data queries returning 2,880 readings per thermostat per day (30-second intervals):

\[\text{Query payload} = 2880 \times 120 \text{ bytes} = 345 \text{ KB per device per day}\]

With 2,000 concurrent users, if 10% query historical data daily, that’s 200 users × 345 KB = 69 MB/day for historical queries. Total bandwidth: 533 + 69 = 602 MB/day, well within most cloud tier limits.

Key Insight: REST API design should follow the principle of resource-oriented design - model your API around nouns (thermostat, temperature, setpoint) not verbs (getTemperature, setTarget). The HTTP methods (GET, PUT, POST, DELETE) provide the verbs. This makes the API self-documenting and consistent across all resources.


Worked Example: Handling Device Offline State in REST APIs

Scenario: A fleet management system has 1,000 GPS trackers on delivery trucks. Some trucks lose cellular connectivity in remote areas. Your REST API must handle requests for offline devices gracefully without confusing mobile app users.

Given:

  • GPS trackers report location every 60 seconds when connected
  • Some trucks go offline for hours in low-coverage areas
  • Mobile dispatch app needs last known location even when device is offline
  • App users must clearly understand device connectivity status

Steps:

  1. Define “offline” threshold and track last-seen timestamp:

    OFFLINE_THRESHOLD_SECONDS = 180  # 3 minutes without heartbeat
    
    def is_device_online(device_id):
        last_seen = get_last_heartbeat(device_id)
        age_seconds = (now() - last_seen).total_seconds()
        return age_seconds < OFFLINE_THRESHOLD_SECONDS
  2. Include connectivity metadata in every response:

    // GET /api/v1/vehicles/truck-42/location
    // Response: 200 OK (even if offline - we have cached data)
    {
      "vehicle_id": "truck-42",
      "latitude": 37.7749,
      "longitude": -122.4194,
      "speed_kmh": 0,
      "heading_degrees": 90,
      "timestamp": "2025-01-15T08:15:00Z",
      "connectivity": {
        "status": "offline",
        "last_seen": "2025-01-15T08:15:00Z",
        "offline_duration_minutes": 135
      }
    }
  3. Use appropriate HTTP status codes for different scenarios:

    @app.route('/api/v1/vehicles/<vehicle_id>/location')
    def get_location(vehicle_id):
        vehicle = db.get_vehicle(vehicle_id)
    
        if not vehicle:
            # Device never registered
            return {"error": "VEHICLE_NOT_FOUND"}, 404
    
        location = cache.get_last_location(vehicle_id)
    
        if not location:
            # Device registered but never reported location
            return {"error": "NO_LOCATION_DATA",
                    "message": "Device has not reported location yet"}, 404
    
        # Return cached location with connectivity status
        # Use 200 OK - we have valid data, just stale
        return {
            "vehicle_id": vehicle_id,
            "latitude": location.lat,
            "longitude": location.lng,
            "timestamp": location.timestamp.isoformat(),
            "connectivity": get_connectivity_status(vehicle_id)
        }, 200

Result: The API returns 200 OK with the last known location and explicit connectivity metadata, allowing the mobile app to display “Last seen 2 hours ago at [location]” rather than showing an error. The 404 status is reserved for truly missing resources (unknown vehicle ID).

Key Insight: For IoT REST APIs, distinguish between “no data” and “stale data”. A device being offline is not an error condition - it’s expected state information. Return cached/stale data with metadata about freshness rather than failing with 503 Service Unavailable. This keeps mobile apps functional even with intermittent device connectivity.

Quick Check: Offline Device Handling


9.6 Key Takeaways

9.7 Summary

REST API Design Principles:

  • Model APIs around resources (nouns), not actions (verbs) – use HTTP methods (GET, PUT, POST, DELETE) as the verbs
  • Organize resource hierarchies: /api/v1/{collection}/{id}/{sub-resource} scales to thousands of devices
  • Include API versioning in the URL path (e.g., /api/v1/) for backward compatibility

Offline Device Handling:

  • Return 200 OK with stale data plus connectivity metadata for offline devices with cached data
  • Reserve 404 for truly missing resources (unknown device ID) and 503 for temporarily unavailable services
  • Include last_seen, offline_duration, and data_freshness fields so clients can make informed decisions

Protocol Selection for IoT:

  • CoAP over UDP: Best for battery-powered sensors with sporadic transmission (4-byte header, no TCP handshake)
  • MQTT over TCP: Best for publish-subscribe patterns with multiple subscribers (dashboards, cloud analytics)
  • Hybrid architectures (CoAP local + MQTT cloud) combine the strengths of both patterns

Common Pitfalls:

  • Returning 503 for offline devices when cached data is available (confuses mobile apps)
  • Using HTTP for battery-constrained sensors (92% protocol overhead for small payloads)
  • Designing verb-based endpoints (/getTemperature) instead of resource-based (/temperature)

9.8 Knowledge Check: Matching and Sequencing

9.9 Quiz: Comprehensive Protocol Review


Scenario: Your IoT REST API receives a request for device data, but the device is offline. What HTTP status code should you return?

Situation Status Code Response Body Reasoning
Device never existed 404 Not Found {"error": "DEVICE_NOT_FOUND"} The resource ID is invalid
Device exists, never reported data 404 Not Found {"error": "NO_DATA_AVAILABLE"} The sub-resource (readings) doesn’t exist yet
Device offline, cached data available 200 OK Data + metadata We have valid (though stale) data to return
Device temporarily unreachable, no cache 503 Service Unavailable {"error": "DEVICE_UNAVAILABLE", "retry_after": 60} The service exists but can’t fulfill request now
Device permanently decommissioned 410 Gone {"error": "DEVICE_DECOMMISSIONED"} The resource existed but no longer does

Example response for offline device with cached data:

GET /api/v1/devices/sensor-042/temperature
Response: 200 OK
{
  "device_id": "sensor-042",
  "temperature_celsius": 22.5,
  "timestamp": "2026-02-07T14:30:00Z",
  "connectivity": {
    "status": "offline",
    "last_seen": "2026-02-07T14:30:00Z",
    "offline_duration_seconds": 3600,
    "data_freshness": "stale"
  }
}

Why 200 OK for stale data?

  • The API request was successful – we returned valid temperature data
  • The client can use the connectivity.status field to decide how to handle staleness
  • Mobile apps can display “Last updated 1 hour ago” without treating it as an error
  • Offline devices are expected in IoT – not an error condition

When to use 503 Service Unavailable:

  • Device is online but unresponsive (hung firmware)
  • No cached data available and request requires live data
  • Cloud gateway can’t reach device network (network partition)
  • Include Retry-After: 60 header to suggest retry timing

Key principle: IoT APIs should distinguish between “resource doesn’t exist” (404), “resource exists but unavailable” (503), and “resource available but data is stale” (200 with metadata).

9.10 Concept Relationships

Understanding how REST API design concepts interconnect:

  • Resource hierarchies determine endpoint structure, which directly impacts HTTP status code selection (404 vs 503 for missing resources)
  • Protocol overhead calculations (CoAP vs MQTT battery impact) inform protocol selection decisions for battery-powered deployments
  • Hybrid architectures (CoAP local + MQTT cloud) leverage strengths of both request-response and publish-subscribe patterns
  • Offline device handling requires distinguishing between no data (404) and stale data (200 with metadata), a pattern that extends to all IoT REST APIs

9.11 Interactive Calculators

9.11.1 REST API Request Load Calculator

Estimate the API request rate and daily bandwidth for your IoT device fleet.

9.11.2 Protocol Overhead Comparison Calculator

Compare the overhead percentage for CoAP, MQTT, and HTTP when sending small IoT payloads.

9.11.3 IoT Battery Life Estimator

Estimate battery life based on protocol choice, transmission frequency, and battery capacity.

9.11.4 REST API Response Size Estimator

Calculate the size of JSON API responses with device metadata and connectivity status fields.

9.11.5 Fleet Bandwidth Planner

Scale bandwidth estimates across fleet sizes and plan for growth.

9.12 See Also

REST API Foundations:

Protocol Selection:

Implementation:

9.13 Try It Yourself

Hands-On Exercise: Design a Smart Parking REST API

Scenario: Create a REST API for a city parking system with 1,000 parking spots across 10 parking lots. Each spot has a magnetic sensor reporting occupied/vacant status.

Requirements:

  • Mobile app needs real-time spot availability
  • City operators need daily/monthly occupancy reports
  • Parking enforcement needs to verify payment for specific spots
  • System must handle sensor offline gracefully

Tasks:

  1. Design the resource hierarchy (parking lots, spots, sensors, reports)
  2. Define HTTP endpoints with proper methods (GET, POST, PUT)
  3. Specify status codes for: spot found occupied, spot not found, sensor offline
  4. Calculate protocol overhead: Compare HTTP vs CoAP for 1,000 sensors reporting every 30 seconds
  5. Design offline device handling: What should GET /lots/3/spots/42/status return if sensor hasn’t reported in 2 hours?

Verification Questions:

  • How many endpoints did you define? (Hint: At least 6-8 for full CRUD operations)
  • Did you distinguish between “spot doesn’t exist” (404) and “sensor offline” (200 with stale data)?
  • What’s the daily bandwidth consumption difference between HTTP and CoAP for your design?

Sample Solution Sketch:

GET    /lots                          # List all parking lots
GET    /lots/{lotId}/spots            # List spots in lot
GET    /lots/{lotId}/spots/{spotId}   # Get specific spot status
PUT    /lots/{lotId}/spots/{spotId}   # Update spot (internal only)
GET    /reports/occupancy?date=...    # Occupancy reports

For offline sensors:
GET /lots/3/spots/42 → 200 OK
{
  "spot_id": 42,
  "status": "occupied",
  "last_updated": "2026-02-09T08:15:00Z",
  "sensor": {
    "status": "offline",
    "last_seen": "2026-02-09T08:15:00Z",
    "offline_duration_minutes": 120
  }
}

9.14 What’s Next?

Chapter Focus Why Read It
REST API Design Patterns Versioning, payload formats, pagination, rate limiting Apply the design principles from this chapter to build production-ready API contracts
Application Protocols Overview Side-by-side comparison of CoAP, MQTT, and HTTP Deepen the protocol selection skills practised in the quizzes above
Protocol Selection Worked Examples Agricultural sensor network end-to-end case study See a full deployment decision — from sensor hardware to cloud — using the same frameworks
Real-time Protocols VoIP, SIP, RTP for audio/video IoT Extend your protocol knowledge to streaming and real-time communication patterns
MQTT Fundamentals Broker architecture, QoS levels, topic design Implement the MQTT cloud-reporting leg of the hybrid architectures covered here
CoAP Fundamentals and Architecture CON/NON messages, observe, block transfer Implement the CoAP local-control leg and understand the confirmable message reliability model