Skip to content

API Changes - December 18, 2025

This document summarizes API changes, new layers, and updates for app developers.

Summary

  • New endpoint field: /api/v1/layers now returns registry version and updated date
  • New layer: michigan-congressional (13 districts)
  • Updated layers: All Michigan layers now use official MICRC redistricting data
  • Total layers: 4 (was 3)

API Changes

GET /api/v1/layers

New response fields: version and updated

{
  "version": "1.0",
  "updated": "2025-12-18",
  "count": 4,
  "layers": [
    {
      "id": "federal-congressional",
      "name": "U.S. Congressional Districts",
      "description": "119th Congress (2025-2027)",
      "scope": "federal",
      "state": null,
      "feature_count": 441
    },
    {
      "id": "michigan-state-house",
      "name": "Michigan State House Districts",
      "description": "Michigan State House districts (110 total) - MICRC 2021 final redistricting",
      "scope": "state",
      "state": "MI",
      "feature_count": 110
    },
    {
      "id": "michigan-state-senate",
      "name": "Michigan State Senate Districts",
      "description": "Michigan State Senate districts (38 total) - MICRC 2021 final redistricting",
      "scope": "state",
      "state": "MI",
      "feature_count": 38
    },
    {
      "id": "michigan-congressional",
      "name": "Michigan Congressional Districts",
      "description": "Michigan Congressional districts (13 total) - MICRC 2021 final redistricting",
      "scope": "state",
      "state": "MI",
      "feature_count": 13
    }
  ]
}

Use cases for version/updated: - Cache invalidation: Compare updated with last fetch - Display "Data as of" in UI - Detect when new layers are added

New Layer: michigan-congressional

Michigan's 13 Congressional districts from the official MICRC redistricting.

Endpoints:

GET /api/v1/layers/michigan-congressional
GET /api/v1/layers/michigan-congressional/features
GET /api/v1/layers/michigan-congressional/features/{district_id}
GET /api/v1/layers/michigan-congressional/geojson

Feature ID format: 2-digit padded string (01, 02, ... 13)

Sample feature:

{
  "id": "01",
  "name": "Congressional District 1",
  "properties": {
    "district": "1",
    "districtn": 1,
    "namelsad": "Congressional District 1",
    "district_id": "01",
    "state": "MI",
    "statefp": "26"
  },
  "geometry": { ... }
}

Note: This layer contains only Michigan districts. For all 441 U.S. districts, use federal-congressional.

Updated Layers

michigan-state-house

Source changed: Michigan GIS Open Data → MICRC

Field Before After
Source Michigan GIS Open Data MICRC
ID Field name district_id
Name Field label namelsad

Breaking change: Feature IDs changed format. - Before: "80", "110" - After: "080", "110" (3-digit padded)

Migration:

// Before
const feature = await fetch('/api/v1/layers/michigan-state-house/features/80');

// After
const feature = await fetch('/api/v1/layers/michigan-state-house/features/080');

michigan-state-senate

Source changed: Census TIGER/Line → MICRC

Field Before After
Source Census TIGER/Line 2024 MICRC
ID Field sldust district_id
Name Field namelsad namelsad

Feature ID format: 3-digit padded string (001, 002, ... 038)

Layer Registry

Current Layers (v1.0)

Layer ID Scope State Features Source
federal-congressional federal - 441 Census TIGER/Line 2024
michigan-state-house state MI 110 MICRC 2021
michigan-state-senate state MI 38 MICRC 2021
michigan-congressional state MI 13 MICRC 2021

Database Files

data/layers/
├── registry.json
├── federal/
│   └── congressional_119.duckdb    # 441 districts
└── mi/
    ├── house.duckdb                # 110 districts
    ├── senate.duckdb               # 38 districts
    └── congressional.duckdb        # 13 districts

Common Properties

All Michigan MICRC layers share these properties:

Property Type Description
district string District number (unpadded)
districtn number District number (numeric)
district_id string Padded district ID (feature key)
namelsad string Full district name
shape.starea() number Area in square feet
shape.stlength() number Perimeter in feet

Code Examples

Fetching Layer List with Version Check

async function checkForUpdates(lastKnownUpdate) {
  const response = await fetch('/api/v1/layers');
  const data = await response.json();

  if (data.updated !== lastKnownUpdate) {
    console.log(`Registry updated: ${data.updated}`);
    // Invalidate caches, refresh layer list
    return { updated: true, layers: data.layers };
  }
  return { updated: false };
}

Loading Michigan Layers

// Get all Michigan layers
const response = await fetch('/api/v1/layers');
const { layers } = await response.json();
const michiganLayers = layers.filter(l => l.state === 'MI');

// Load each as GeoJSON for MapLibre
for (const layer of michiganLayers) {
  const geojson = await fetch(`/api/v1/layers/${layer.id}/geojson`).then(r => r.json());
  map.addSource(layer.id, { type: 'geojson', data: geojson });
}

Layer Switcher UI

// Build layer selector from API
async function buildLayerSelector() {
  const { version, updated, layers } = await fetch('/api/v1/layers').then(r => r.json());

  const select = document.getElementById('layer-select');
  select.innerHTML = '';

  // Group by scope
  const federal = layers.filter(l => l.scope === 'federal');
  const state = layers.filter(l => l.scope === 'state');

  if (federal.length) {
    const group = document.createElement('optgroup');
    group.label = 'Federal';
    federal.forEach(l => {
      const opt = document.createElement('option');
      opt.value = l.id;
      opt.textContent = `${l.name} (${l.feature_count})`;
      group.appendChild(opt);
    });
    select.appendChild(group);
  }

  if (state.length) {
    const group = document.createElement('optgroup');
    group.label = 'State (Michigan)';
    state.forEach(l => {
      const opt = document.createElement('option');
      opt.value = l.id;
      opt.textContent = `${l.name} (${l.feature_count})`;
      group.appendChild(opt);
    });
    select.appendChild(group);
  }

  // Show version info
  document.getElementById('data-version').textContent = `v${version} (${updated})`;
}

Data Sources

MICRC (Michigan Independent Citizens Redistricting Commission)

All Michigan layers now use official MICRC 2021 final redistricting boundaries.

ArcGIS REST Services: - Base: https://gisagocss.state.mi.us/arcgis/rest/services/CSS/CSS_MDOS_MICRC_Final/MapServer - Congressional: /17 - State Senate: /16 - State House: /15

Web Viewer: https://www.arcgis.com/apps/webappviewer/index.html?id=5e383a06df1d4c44a222a8d442534ebb

Census TIGER/Line

Federal congressional districts use Census Bureau boundaries.

URL: https://www2.census.gov/geo/tiger/TIGER2024/CD/

Changelog

2025-12-18

  • Added version and updated fields to /api/v1/layers response
  • Added michigan-congressional layer (13 districts)
  • Updated michigan-state-house to MICRC source (110 districts)
  • Updated michigan-state-senate to MICRC source (38 districts)
  • Changed Michigan feature ID format to padded strings

2025-12-17

  • Added michigan-state-senate layer (Census source)
  • Added michigan-state-house layer (Michigan GIS Open Data)

2025-12-16

  • Initial multi-layer API release
  • Added /api/v1/layers/* endpoints
  • Created federal-congressional layer