Skip to content

Release: Multi-Layer API Endpoints

Date: December 17, 2025 Version: 1.1.0

Summary

New /api/v1/layers/* endpoints for serving multiple geographic boundary layers. Each layer is an independent DuckDB database, allowing state-specific data to be added without affecting existing endpoints.

First new layer: Michigan State House Districts (110 districts)

New Endpoints

Method Endpoint Description
GET /api/v1/layers List all available layers
GET /api/v1/layers/{layer_id} Layer metadata and schema
GET /api/v1/layers/{layer_id}/features Paginated feature list
GET /api/v1/layers/{layer_id}/features/{id} Single feature with geometry
GET /api/v1/layers/{layer_id}/geojson Full GeoJSON FeatureCollection

Available Layers

Layer ID Name Features Scope
federal-congressional U.S. Congressional Districts 441 Federal
michigan-state-house Michigan State House Districts 110 State (MI)

Quick Start

List All Layers

curl https://districts.nominate.ai/api/v1/layers
{
  "count": 2,
  "layers": [
    {
      "id": "federal-congressional",
      "name": "U.S. Congressional Districts",
      "scope": "federal",
      "feature_count": 441
    },
    {
      "id": "michigan-state-house",
      "name": "Michigan State House Districts",
      "scope": "state",
      "state": "MI",
      "feature_count": 110
    }
  ]
}

Get Layer GeoJSON (for Map Rendering)

curl "https://districts.nominate.ai/api/v1/layers/michigan-state-house/geojson?include_geometry=true"
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": "001",
        "name": "001",
        "legislator": "Tenisha Yancey",
        "party": "Democrat",
        "peninsula": "lower",
        "url": "https://housedems.com/yancey"
      },
      "geometry": { "type": "Polygon", "coordinates": [...] }
    }
  ]
}

Get Single Feature

curl https://districts.nominate.ai/api/v1/layers/michigan-state-house/features/080
{
  "id": "080",
  "name": "080",
  "properties": {
    "legislator": "Mary Whiteford",
    "party": "Republican",
    "peninsula": "lower",
    "url": "https://gophouse.org/representatives/southwest/whiteford/",
    "sqmiles": 733.917
  },
  "geometry": { "type": "Polygon", "coordinates": [...] }
}

Frontend Integration

Loading a Layer on the Map

// Fetch available layers
const layersResponse = await fetch('/api/v1/layers');
const { layers } = await layersResponse.json();

// Load Michigan State House layer
async function loadLayer(layerId) {
  const response = await fetch(`/api/v1/layers/${layerId}/geojson?include_geometry=true`);
  const geojson = await response.json();

  // Add to MapLibre
  map.addSource(layerId, { type: 'geojson', data: geojson });
  map.addLayer({
    id: `${layerId}-fill`,
    source: layerId,
    type: 'fill',
    paint: {
      'fill-color': ['match', ['get', 'party'],
        'Republican', '#E91D0E',
        'Democrat', '#232066',
        '#808080'
      ],
      'fill-opacity': 0.6
    }
  });
}

// Load the layer
loadLayer('michigan-state-house');

Layer Switcher UI

// Build layer selector from API response
layers.forEach(layer => {
  const option = document.createElement('option');
  option.value = layer.id;
  option.textContent = `${layer.name} (${layer.feature_count})`;
  layerSelect.appendChild(option);
});

layerSelect.addEventListener('change', (e) => {
  clearCurrentLayer();
  loadLayer(e.target.value);
});

Michigan State House Properties

Property Type Description
id string District number (e.g., "080")
name string District label
legislator string Current representative
party string "Republican" or "Democrat"
url string Legislator's website
peninsula string "upper" or "lower"
sqmiles number District area

Backwards Compatibility

Existing endpoints unchanged. The current /api/v1/districts/* endpoints continue to work exactly as before. Apps can migrate to the new layer system at their own pace.

Old Endpoint New Equivalent
/api/v1/districts?format=geojson /api/v1/layers/federal-congressional/geojson
/api/v1/districts/{geoid} /api/v1/layers/federal-congressional/features/{geoid}

Caching

All layer endpoints use Redis caching: - Layer metadata: 1 hour - GeoJSON/Features: 7 days

Coming Soon

  • Michigan State Senate Districts
  • Iowa State House/Senate
  • Kentucky State House/Senate
  • County boundaries

Questions?

Contact the Districts Service team or see the full API docs at: https://docs.nominate.ai/cbdistricts-docs/district-pages-api/