Jurisdiction API - Implementation Guide
Practical examples and patterns for integrating with the Jurisdiction API
Table of Contents
- Jurisdiction API - Implementation Guide
- Table of Contents
- Recommended Pattern: Single Call Approach
- Drill-Down Pattern: Getting Detailed Information
- Common Usage Patterns
- Best Practices
- Response Format Examples
- Support
Recommended Pattern: Single Call Approach
Get all jurisdiction data with one API call using the consolidated endpoint.
The /api/jurisdictions/lookup endpoint returns all regions, jurisdictions, and counts in a single response - perfect for dashboards and overview pages.
JavaScript/React
React Hook Example
import { useQuery } from '@tanstack/react-query';
function JurisdictionDashboard() {
const API_KEY = process.env.REACT_APP_JURISDICTION_API_KEY;
const { data, isLoading, error } = useQuery({
queryKey: ['jurisdiction-lookup'],
queryFn: async () => {
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch jurisdiction data');
}
return response.json();
}
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Jurisdiction Overview</h1>
{/* Display totals */}
<div className="stats">
<div>Total Jurisdictions: {data.totals.jurisdictions}</div>
<div>Total Agencies: {data.totals.agencies}</div>
<div>Total Contributors: {data.totals.contributors}</div>
<div>Total Sources: {data.totals.sources}</div>
</div>
{/* Display regions and jurisdictions */}
{data.regions.map(region => (
<div key={region.id}>
<h2>{region.name}</h2>
{region.jurisdictions.map(jurisdiction => (
<div key={jurisdiction.id}>
<h3>{jurisdiction.name}</h3>
<p>Agencies: {jurisdiction.agencyCount}</p>
<p>Contributors: {jurisdiction.contributorCount}</p>
<p>Sources: {jurisdiction.sourceCount}</p>
</div>
))}
</div>
))}
</div>
);
}
Plain JavaScript Example
async function fetchJurisdictionData() {
const API_KEY = 'your_api_key_here';
try {
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Access the data
console.log('Total jurisdictions:', data.totals.jurisdictions);
// Loop through regions
data.regions.forEach(region => {
console.log(`Region: ${region.name}`);
region.jurisdictions.forEach(jurisdiction => {
console.log(` - ${jurisdiction.name}`);
console.log(` Agencies: ${jurisdiction.agencyCount}`);
console.log(` Contributors: ${jurisdiction.contributorCount}`);
console.log(` Sources: ${jurisdiction.sourceCount}`);
});
});
return data;
} catch (error) {
console.error('Error fetching jurisdiction data:', error);
throw error;
}
}
// Use it
fetchJurisdictionData()
.then(data => {
// Do something with the data
});
Node.js
Node.js with Fetch
// Using built-in fetch (Node 18+)
const API_KEY = process.env.JURISDICTION_API_KEY;
async function getJurisdictionLookup() {
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
return data;
}
// Example: Find jurisdiction by name
async function findJurisdiction(jurisdictionName) {
const data = await getJurisdictionLookup();
for (const region of data.regions) {
const jurisdiction = region.jurisdictions.find(
j => j.name.toLowerCase() === jurisdictionName.toLowerCase()
);
if (jurisdiction) {
return {
jurisdiction,
region: region.name
};
}
}
return null;
}
// Example: Get all jurisdictions with more than X agencies
async function getJurisdictionsWithMinAgencies(minAgencies) {
const data = await getJurisdictionLookup();
const results = [];
data.regions.forEach(region => {
region.jurisdictions.forEach(jurisdiction => {
if (jurisdiction.agencyCount >= minAgencies) {
results.push({
name: jurisdiction.name,
region: region.name,
agencyCount: jurisdiction.agencyCount
});
}
});
});
return results;
}
// Usage
findJurisdiction('Los Angeles')
.then(result => {
if (result) {
console.log(`Found in region: ${result.region}`);
console.log(`Agencies: ${result.jurisdiction.agencyCount}`);
}
});
Node.js with Axios
const axios = require('axios');
const api = axios.create({
baseURL: 'https://your-app.replit.app',
headers: {
'Authorization': `Bearer ${process.env.JURISDICTION_API_KEY}`
}
});
async function getJurisdictionLookup() {
try {
const response = await api.get('/api/jurisdictions/lookup');
return response.data;
} catch (error) {
if (error.response) {
console.error('API Error:', error.response.status, error.response.data);
} else if (error.request) {
console.error('Network Error:', error.message);
} else {
console.error('Error:', error.message);
}
throw error;
}
}
// Export for use in other modules
module.exports = { getJurisdictionLookup };
Python
Python with Requests
import requests
import os
from typing import Dict, List, Optional
class JurisdictionAPI:
def __init__(self):
self.base_url = 'https://your-app.replit.app'
self.api_key = os.environ.get('JURISDICTION_API_KEY')
self.headers = {
'Authorization': f'Bearer {self.api_key}'
}
def get_jurisdiction_lookup(self) -> Dict:
"""Get all jurisdiction data with counts"""
response = requests.get(
f'{self.base_url}/api/jurisdictions/lookup',
headers=self.headers
)
response.raise_for_status()
return response.json()
def find_jurisdiction(self, jurisdiction_name: str) -> Optional[Dict]:
"""Find a jurisdiction by name"""
data = self.get_jurisdiction_lookup()
for region in data['regions']:
for jurisdiction in region['jurisdictions']:
if jurisdiction['name'].lower() == jurisdiction_name.lower():
return {
'jurisdiction': jurisdiction,
'region': region['name']
}
return None
def get_region_summary(self, region_name: str) -> Dict:
"""Get summary statistics for a region"""
data = self.get_jurisdiction_lookup()
for region in data['regions']:
if region['name'].lower() == region_name.lower():
total_agencies = sum(j['agencyCount'] for j in region['jurisdictions'])
total_contributors = sum(j['contributorCount'] for j in region['jurisdictions'])
total_sources = sum(j['sourceCount'] for j in region['jurisdictions'])
return {
'region': region['name'],
'jurisdictions': len(region['jurisdictions']),
'agencies': total_agencies,
'contributors': total_contributors,
'sources': total_sources
}
return None
# Usage Example
if __name__ == '__main__':
api = JurisdictionAPI()
# Get all data
data = api.get_jurisdiction_lookup()
print(f"Total jurisdictions: {data['totals']['jurisdictions']}")
# Find specific jurisdiction
result = api.find_jurisdiction('San Francisco')
if result:
print(f"Found in region: {result['region']}")
print(f"Agencies: {result['jurisdiction']['agencyCount']}")
# Get region summary
summary = api.get_region_summary('California')
if summary:
print(f"Region {summary['region']} has:")
print(f" - {summary['jurisdictions']} jurisdictions")
print(f" - {summary['agencies']} agencies")
Drill-Down Pattern: Getting Detailed Information
When you need complete details about agencies, contributors, or sources beyond just counts.
Get Agencies
Fetch all agencies for a jurisdiction with complete details.
// JavaScript Example
async function getAgenciesForJurisdiction(jurisdictionId) {
const API_KEY = process.env.JURISDICTION_API_KEY;
const response = await fetch(
`https://your-app.replit.app/api/jurisdictions/${jurisdictionId}/agencies`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
if (!response.ok) {
throw new Error('Failed to fetch agencies');
}
const agencies = await response.json();
return agencies;
}
// Usage: First get the jurisdiction ID from lookup, then fetch agencies
async function getAgenciesByJurisdictionName(jurisdictionName) {
// Step 1: Get lookup data to find the jurisdiction ID
const lookupResponse = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const lookupData = await lookupResponse.json();
// Step 2: Find the jurisdiction
let jurisdictionId = null;
for (const region of lookupData.regions) {
const jurisdiction = region.jurisdictions.find(
j => j.name === jurisdictionName
);
if (jurisdiction) {
jurisdictionId = jurisdiction.id;
break;
}
}
if (!jurisdictionId) {
throw new Error(`Jurisdiction "${jurisdictionName}" not found`);
}
// Step 3: Fetch the agencies
const agencies = await getAgenciesForJurisdiction(jurisdictionId);
return agencies;
}
// Example
getAgenciesByJurisdictionName('Los Angeles')
.then(agencies => {
agencies.forEach(agency => {
console.log(`Agency: ${agency.name}`);
console.log(` Original ID: ${agency.original_id}`);
});
});
Get Contributors
Fetch all contributors for a jurisdiction.
# Python Example
import requests
def get_contributors_for_jurisdiction(jurisdiction_id: str, api_key: str):
"""Get all contributors for a specific jurisdiction"""
response = requests.get(
f'https://your-app.replit.app/api/jurisdictions/{jurisdiction_id}/contributors',
headers={'Authorization': f'Bearer {api_key}'}
)
response.raise_for_status()
return response.json()
# Complete example: Get contributors by jurisdiction name
def get_contributors_by_name(jurisdiction_name: str, api_key: str):
# First get the lookup data
lookup_response = requests.get(
'https://your-app.replit.app/api/jurisdictions/lookup',
headers={'Authorization': f'Bearer {api_key}'}
)
lookup_data = lookup_response.json()
# Find the jurisdiction ID
jurisdiction_id = None
for region in lookup_data['regions']:
for jurisdiction in region['jurisdictions']:
if jurisdiction['name'] == jurisdiction_name:
jurisdiction_id = jurisdiction['id']
break
if jurisdiction_id:
break
if not jurisdiction_id:
raise ValueError(f'Jurisdiction "{jurisdiction_name}" not found')
# Get the contributors
contributors = get_contributors_for_jurisdiction(jurisdiction_id, api_key)
return contributors
# Usage
api_key = os.environ['JURISDICTION_API_KEY']
contributors = get_contributors_by_name('San Francisco', api_key)
for contributor in contributors:
print(f"Contributor: {contributor['name']}")
print(f" ID: {contributor['id']}")
Get Sources
Fetch all sources for a contributor.
// Node.js Example
async function getSourcesForContributor(contributorId) {
const API_KEY = process.env.JURISDICTION_API_KEY;
const response = await fetch(
`https://your-app.replit.app/api/contributors/${contributorId}/sources`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to fetch sources: ${response.statusText}`);
}
return await response.json();
}
// Complete workflow: Get all sources for a specific contributor
async function getAllSourcesForContributor(contributorName, jurisdictionName) {
const API_KEY = process.env.JURISDICTION_API_KEY;
// Step 1: Get lookup data
const lookupResponse = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const lookupData = await lookupResponse.json();
// Step 2: Find jurisdiction ID
let jurisdictionId = null;
for (const region of lookupData.regions) {
const jurisdiction = region.jurisdictions.find(j => j.name === jurisdictionName);
if (jurisdiction) {
jurisdictionId = jurisdiction.id;
break;
}
}
if (!jurisdictionId) {
throw new Error(`Jurisdiction "${jurisdictionName}" not found`);
}
// Step 3: Get contributors for that jurisdiction
const contributorsResponse = await fetch(
`https://your-app.replit.app/api/jurisdictions/${jurisdictionId}/contributors`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const contributors = await contributorsResponse.json();
// Step 4: Find the specific contributor
const contributor = contributors.find(c => c.name === contributorName);
if (!contributor) {
throw new Error(`Contributor "${contributorName}" not found`);
}
// Step 5: Get sources for that contributor
const sources = await getSourcesForContributor(contributor.id);
return sources;
}
// Usage
getAllSourcesForContributor('John Doe', 'Los Angeles')
.then(sources => {
console.log(`Found ${sources.length} sources`);
sources.forEach(source => {
console.log(` - ${source.name}`);
});
})
.catch(error => console.error(error));
Common Usage Patterns
Pattern 1: Caching Lookup Data
Cache the lookup data to minimize API calls and improve performance.
class JurisdictionCache {
constructor(apiKey, cacheTimeMs = 5 * 60 * 1000) { // 5 minute default
this.apiKey = apiKey;
this.cacheTimeMs = cacheTimeMs;
this.cache = null;
this.cacheTimestamp = null;
}
async getLookupData() {
const now = Date.now();
// Return cached data if still valid
if (this.cache && this.cacheTimestamp) {
if (now - this.cacheTimestamp < this.cacheTimeMs) {
return this.cache;
}
}
// Fetch fresh data
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: { 'Authorization': `Bearer ${this.apiKey}` }
});
if (!response.ok) {
throw new Error('Failed to fetch jurisdiction data');
}
this.cache = await response.json();
this.cacheTimestamp = now;
return this.cache;
}
invalidateCache() {
this.cache = null;
this.cacheTimestamp = null;
}
}
// Usage
const cache = new JurisdictionCache(process.env.JURISDICTION_API_KEY);
// First call fetches from API
const data1 = await cache.getLookupData();
// Subsequent calls use cache (within 5 minutes)
const data2 = await cache.getLookupData(); // Returns cached data
// Force refresh
cache.invalidateCache();
const data3 = await cache.getLookupData(); // Fetches fresh data
Pattern 2: Search and Filter
Implement search functionality across jurisdictions.
async function searchJurisdictions(searchTerm) {
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: { 'Authorization': `Bearer ${process.env.JURISDICTION_API_KEY}` }
});
const data = await response.json();
const results = [];
const search = searchTerm.toLowerCase();
data.regions.forEach(region => {
region.jurisdictions.forEach(jurisdiction => {
// Search by jurisdiction name or region name
if (jurisdiction.name.toLowerCase().includes(search) ||
region.name.toLowerCase().includes(search)) {
results.push({
...jurisdiction,
regionName: region.name,
regionId: region.id
});
}
});
});
return results;
}
// Usage
const results = await searchJurisdictions('san');
// Returns: San Francisco, San Diego, San Jose, etc.
Pattern 3: Generate Statistics
Calculate custom statistics from the lookup data.
async function generateStatistics() {
const response = await fetch('https://your-app.replit.app/api/jurisdictions/lookup', {
headers: { 'Authorization': `Bearer ${process.env.JURISDICTION_API_KEY}` }
});
const data = await response.json();
const stats = {
// Overall totals (already provided)
totals: data.totals,
// Per-region breakdown
regionBreakdown: data.regions.map(region => ({
name: region.name,
jurisdictionCount: region.jurisdictions.length,
totalAgencies: region.jurisdictions.reduce((sum, j) => sum + j.agencyCount, 0),
totalContributors: region.jurisdictions.reduce((sum, j) => sum + j.contributorCount, 0),
totalSources: region.jurisdictions.reduce((sum, j) => sum + j.sourceCount, 0)
})),
// Top jurisdictions by agency count
topJurisdictionsByAgencies: data.regions
.flatMap(region =>
region.jurisdictions.map(j => ({
...j,
regionName: region.name
}))
)
.sort((a, b) => b.agencyCount - a.agencyCount)
.slice(0, 10),
// Average agencies per jurisdiction
avgAgenciesPerJurisdiction: data.totals.agencies / data.totals.jurisdictions
};
return stats;
}
// Usage
const stats = await generateStatistics();
console.log(`Average agencies per jurisdiction: ${stats.avgAgenciesPerJurisdiction.toFixed(2)}`);
console.log('Top 10 jurisdictions:', stats.topJurisdictionsByAgencies);
Pattern 4: Robust Error Handling
Handle common error scenarios gracefully.
async function fetchWithErrorHandling(url, apiKey, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
// Handle different HTTP status codes
if (response.status === 401) {
throw new Error('Invalid API key. Please check your credentials.');
}
if (response.status === 404) {
throw new Error('Resource not found. Check the endpoint URL.');
}
if (response.status === 429) {
// Rate limited - wait and retry
if (attempt < retries) {
const waitTime = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Rate limited. Retrying in ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
throw new Error('Rate limit exceeded. Please try again later.');
}
if (response.status >= 500) {
// Server error - retry
if (attempt < retries) {
console.log(`Server error. Retry attempt ${attempt}/${retries}`);
await new Promise(resolve => setTimeout(resolve, 1000));
continue;
}
throw new Error('Server error. Please try again later.');
}
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === retries) {
throw error;
}
// Network error - retry
console.log(`Network error. Retry attempt ${attempt}/${retries}`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
// Usage
try {
const data = await fetchWithErrorHandling(
'https://your-app.replit.app/api/jurisdictions/lookup',
process.env.JURISDICTION_API_KEY
);
console.log('Data fetched successfully');
} catch (error) {
console.error('Failed to fetch data:', error.message);
// Handle error appropriately (show user message, log to monitoring, etc.)
}
Best Practices
Use Environment Variables for API Keys
Never hardcode API keys in your source code. Use environment variables (process.env.JURISDICTION_API_KEY) and keep them out of version control.
// ✅ GOOD
const API_KEY = process.env.JURISDICTION_API_KEY;
// ❌ BAD
const API_KEY = 'sk_live_abc123def456'; // Never do this!
Cache Lookup Data
The jurisdiction lookup data doesn't change frequently. Cache it on the client side for 5-10 minutes to reduce API calls and improve performance.
// Use a caching layer like the JurisdictionCache class shown above
const cache = new JurisdictionCache(apiKey, 5 * 60 * 1000); // 5 minutes
Use the Consolidated Endpoint First
Start with /api/jurisdictions/lookup to get overview data. Only use the detailed endpoints when you need complete lists of agencies, contributors, or sources.
// ✅ GOOD - Single call for overview
const data = await fetch('/api/jurisdictions/lookup');
// ❌ LESS EFFICIENT - Multiple calls for the same data
const regions = await fetch('/api/regions');
for (const region of regions) {
const jurisdictions = await fetch(`/api/regions/${region.id}/jurisdictions`);
// ... more calls
}
Implement Error Handling
Always handle potential errors: invalid API keys (401), rate limits (429), server errors (500+), and network failures. Use retries with exponential backoff for transient errors.
// Use the fetchWithErrorHandling function shown above
const data = await fetchWithErrorHandling(url, apiKey, 3);
Respect Rate Limits
Implement caching and avoid making unnecessary API calls in loops. If you need to fetch data for multiple jurisdictions, use the consolidated endpoint to get everything at once.
// ✅ GOOD - One call gets all data
const allData = await fetch('/api/jurisdictions/lookup');
// ❌ BAD - Loop making many API calls
for (const jurisdictionId of jurisdictionIds) {
await fetch(`/api/jurisdictions/${jurisdictionId}/agencies`); // Too many calls!
}
Response Format Examples
Consolidated Lookup Response
{
"regions": [
{
"id": "uuid",
"original_id": 1,
"name": "Region Name",
"jurisdictions": [
{
"id": "uuid",
"original_id": 1,
"name": "Jurisdiction Name",
"agencychannel": 123,
"jurisdictionchannel": 456,
"agencyCount": 5,
"contributorCount": 3,
"sourceCount": 8
}
]
}
],
"totals": {
"jurisdictions": 10,
"agencies": 50,
"contributors": 30,
"sources": 85
}
}
Agency Details Response
[
{
"id": "uuid",
"original_id": 1,
"name": "Agency Name",
"jurisdiction_name": "Jurisdiction Name",
"region_name": "Region Name"
}
]
Support
For API documentation and reference, visit:
- API Documentation:
https://your-app.replit.app/api-docs - Implementation Guide:
https://your-app.replit.app/implementation-guide
For API key management, log in to your admin dashboard.
Last Updated: November 2025