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¶
{
"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¶
{
"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/