Version: 1.0
Generated: 2025-08-18
The Atrium Demographics API resolves people/households to demographic records and returns a Mosaic household code and persona. It supports single and batch lookups, optimized for high-volume requests.
Base URL (example)
http://localhost:8000
Primary endpoints
POST /lookup — single lookup (one LookupRequest -> LookupResponse)POST /lookup/batch — batch lookup (array of LookupRequest -> array of LookupResponse / ErrorResponse)POST /admin/keys — create API keys (requires admin key header)X-API-KEY: <api_key>db.api_keys (is_active: true required).api_key_hash = "sha256:" + HMAC_SHA256(API_KEY_SALT, api_key)
API_KEY_SALT comes from .env.Example header
-H "X-API-KEY: key-prod-atrium-website-001"
X-ADMIN-KEY: <admin_key> (matches environment ADMIN_API_KEY)X-Debug: true — returns candidate lists/query diagnostics (dev-only; avoid in production).Accepts both lat/lon and latitude/longitude. The server normalizes them.
{
"uid": "Optional[string] (client correlation id)",
"first_name": "Optional[string]",
"last_name": "Optional[string]",
"address": "Optional[string] (format: 'Street, City, ST 5digit')",
"birth_year_month": "Optional[string] (e.g. '199602' or '1996-02')",
"lat": "Optional[number] (or 'latitude')",
"lon": "Optional[number] (or 'longitude')",
"operator": "Optional['and'|'or'] (default: 'and')"
}
Notes:
address parsing expects "Street, City, ST ZIP" and extracts state/ZIP.lat/lon provided, the API will normalize to the canonical names.{
"matched": boolean,
"records": [
{
"full_name": "string",
"lat": number,
"lon": number,
"persona": {
"mosaic_group_name": "string",
"mosaic_group_code": "string",
"mosaic_type_name": "string",
"mosaic_type_code": "string",
"description": "string"
},
"raw_data": { /* full demographic record from Mongo */ }
}
]
}
matched: false, records will usually be an empty array (or an aggregate/neighbors summary if implemented).{
"error_code": number,
"message": "string",
"details": "optional string",
"request_id": "string (server correlation id)"
}
Common error codes
1000 — VALIDATION_ERROR (422)1001 — AUTH_ERROR (401/403)1002 — DB_ERROR (503)1003 — NOT_FOUND1004 — RATE_LIMIT1099 — INTERNAL_ERROR (500)1100 — PARTIAL_FAILURE (batch partial results)When a lookup arrives the API attempts cases in order and returns the first result set produced. Send as much data as you have; the service will fallback sensibly.
Case 1 (highest priority): latitude/longitude + last_name + DOB
lat/lon, last_name, birth_year_monthCase 2: latitude/longitude + DOB
lat/lon, birth_year_monthCase 3: latitude/longitude + last_name
lat/lon, last_nameCase 4: last_name + DOB + state (address parsed).
Case 5 (fallback): latitude/longitude only — neighborhood aggregate/blockgroup sampling
Notes
/lookup/batch and chunk large jobs (e.g., 500–2000 per request).POST /lookup/batch)LookupRequest objects (NOT an object with requests key).LookupResponse or ErrorResponse.Current indexes inspected
_id_idx_birth_year_month — { "BIRTH YEAR MONTH V2": 1 }idx_name_address — { "FIRST NAME": 1, "SURNAME": 1, "PRIMARY ADDRESS": 1 }idx_strict_lookup_v2 — compound index:{
"FIRST NAME": 1,
"SURNAME": 1,
"PRIMARY ADDRESS": 1,
"BIRTH YEAR MONTH V2": 1,
"STATE ABBREVIATION": 1,
"FIPS ZIP CODE": 1
}
This uses collation { locale: "en", strength: 2 } for case-insensitive matching.
Recommended geo improvements
location GeoJSON field and a 2dsphere index:db.demographics.updateMany({}, [
{ $set: { location: { type: "Point", coordinates: [{ $toDouble: "$Lon" }, { $toDouble: "$Lat" }] } } }
])
db.demographics.createIndex({ location: "2dsphere" })
$near / $geoNear for radius-based queries (meters precision).All requests are audited as JSON-lines in requests.log. Other logs:
matched.log — human readable matched eventserrors.log — warnings and stack tracesAudit entry example
{
"ts": "2025-08-18T17:23:12.123Z",
"rid": "9f8a4c...",
"item_rid": "b1e2...",
"client_ip": "198.51.100.10",
"user_agent": "PostmanRuntime/7.29.0",
"method": "POST /lookup",
"status": 200,
"latency_ms": 36.4,
"api_key_hash": "sha256:3a7fd3...",
"uid": "client-123",
"payload_summary": { "last_name": true, "lat": true, "lon": true },
"result": { "matched": true, "method": "nested", "mosaic": "A05" },
"error_code": null,
"error_msg": null,
"service": "atrium-demographics"
}
api_key_hash mapping
Compute HMAC-SHA256 with API_KEY_SALT (from .env) to map api_key_hash back to known keys without storing raw keys.
Host log path
./api/logs on the host (container path commonly /app/logs).200 OK — processed (matched true/false)400 Bad Request — malformed request or bad shape401/403 — authentication failure (invalid API key/admin key)422 Unprocessable Entity — Pydantic validation error503 Service Unavailable — DB connectivity issues500 Internal Server Error — unexpected server errorAll error responses include an ErrorResponse JSON plus request_id for tracing.
curl -X POST 'http://localhost:8000/lookup' -H "Content-Type: application/json" -H "X-API-KEY: key-prod-atrium-website-001" -d '{
"uid": "client-123",
"last_name": "Smith",
"birth_year_month": "199612",
"latitude": 42.193963,
"longitude": -70.959226
}'
curl -X POST 'http://localhost:8000/lookup/batch' -H "Content-Type: application/json" -H "X-API-KEY: key-prod-atrium-website-001" -d '[
{ "uid": "u1", "last_name": "Motta", "birth_year_month":"199612", "latitude":42.193963, "longitude":-70.959226 },
{ "uid": "u2", "latitude":42.193963, "longitude":-70.959226 }
]'
curl -X POST 'http://localhost:8000/admin/keys' -H "Content-Type: application/json" -H "X-ADMIN-KEY: adm-key-b3c8a2e1f4d9a7b6e0c5d3f2a1b4e9c8" -d '{ "client_name":"atrium-website", "tenant_id":"website" }'
base_url, api_key, uid_prefix.uid per item. Use the robust Tests snippet to avoid pm.response crashes.Each case includes input and asserts.
Case 1 — Exact household (happy path)
uid, last_name, birth_year_month, latitude, longitudematched: true, records[0].raw_data.HH_ZeroBasedRecordID presentCase 2 — DOB + geo
birth_year_month, latitude, longitudematched true if unique; otherwise neighbors summaryCase 3 — Surname + geo
last_name, latitude, longitudematched if unique otherwise aggregateCase 4 — Surname + DOB + state/address
address: "123 Main St, Austin, TX 78701", last_name, birth_year_monthCase 5 — Geo only
latitude, longitudematched:false with neighborhood mosaic countsLarge batch
Invalid API key
X-API-KEY: baderror_code:1001Validation errors
error_code:1000DB down
error_code:1002Audit presence
requests.log should contain JSON lines with uid, api_key_hash, a rid, and timing..env and secret materials. Use a secrets manager in production.2dsphere index and migration for accurate radius searches.X-Debug to return candidate docs (dev only)..env.sample (no secrets) showing the required variables (API_KEY_SALT, ADMIN_API_KEY, MONGO_URL, DB_NAME, COLLECTION, LOG_DIR, etc.).mongo-init/init-indexes.js).raw_data JSON from the demographics collection for field reference.