Skip to content

Endpoints

Every route is a GET under https://georgiacivicdata.org/api/v1, except the school-zone lookup, which also accepts a POST. Per-dataset filter parameters are listed on each dataset page.

At a glance

EndpointPurpose
/datasetsList every dataset + dimension
/datasets/{main}/{topic}Full schema for one dataset (JSON)
/datasets/{main}/{topic}/contractRaw ODCS contract (YAML/JSON)
/{main}/{topic}Query a dataset’s rows
/education/_dimensions/{name}Read an education dimension (districts/schools/demographics)
/_dimensions/{name}Read a global dimension (counties/demographics)
/education/_dimensions/{name}/schemaA dimension’s schema
/education/school_attendance_zonesSchool attendance zone polygons, plus /schema and /coverage
/education/school_attendance_zones/lookupZones at a point or address, or covering a census tract (GET or POST)
/education/school_district_boundariesSchool district boundaries (Census TIGER/Line)

Catalog

GET/api/v1/datasets

One summary per dataset (title, source, year range, detail levels, endpoint) plus a dimensions block. No parameters.

curl "https://georgiacivicdata.org/api/v1/datasets"

Dataset schema

GET/api/v1/datasets/{main_topic}/{topic}

The full schema: every column (name, type, role, unit, range, allowed values, description), the structured filters list, foreign keys, the key_metric pointer, available years, and the schema_hash. This is what powers these docs.

curl "https://georgiacivicdata.org/api/v1/datasets/education/act_scores"
GET/api/v1/datasets/{main_topic}/{topic}/contract

The authoritative ODCS v3.2 contract. ?format=yaml (default) returns it verbatim; ?format=json returns it parsed.

curl "https://georgiacivicdata.org/api/v1/datasets/education/act_scores/contract?format=yaml"

Dataset query

GET/api/v1/{main_topic}/{topic}

Query a dataset’s rows. Common parameters:

ParameterDescription
yearExact year. Mutually exclusive with the range below.
year_min / year_maxInclusive year range.
detailstates / counties / districts / schools (whatever the dataset publishes).
district_code / school_code / county_fips / demographicComma-separated code lists (where the dataset has the column).
per-categorical paramsOne parameter per categorical column (see the dataset page).
limit / offsetPage size (default 1000, max 10000) and offset.
formatjson (default) / csv / parquet.
curl "https://georgiacivicdata.org/api/v1/education/act_scores?year=2024&test_component=composite&detail=districts"

Dimensions

GET/api/v1/education/_dimensions/{name}

Read an education-scoped lookup table — districts, schools, or demographics — paginated with limit / offset. These supply the label columns the fact queries join in.

curl "https://georgiacivicdata.org/api/v1/education/_dimensions/districts?limit=10"
GET/api/v1/_dimensions/{name}

Read a global lookup table — counties (all 159 Georgia counties, FIPS code + name) or demographics. The education route above remains for districts, schools, and demographics.

curl "https://georgiacivicdata.org/api/v1/_dimensions/counties?limit=10"
GET/api/v1/education/_dimensions/{name}/schema

A dimension’s primary key, attributes, and link keys.

School attendance zones

Attendance zones are the areas a school district uses to assign an address to a school, by level (elementary, middle, high) and grade span. The zones are reconstructed from published district and vendor sources of varying currency and accuracy, and some districts have no zones (see /coverage). A zone or lookup is not an official enrollment determination: confirm results with the school district.

GET/api/v1/education/school_attendance_zones

Zone polygons, one per school, level, and grade span. Parameters:

ParameterDescription
district_code / school_code / zone_levelComma-separated lists. zone_level is elementary, middle, or high.
gradepk, k, or 01–12: only zones that serve that grade.
yearSchool year the zones are in effect for, as its ending year (2027 = 2026–27).
formatgeojson (default) / shapefile (a zip with one layer per level) / geoparquet / json (attributes only, no geometry).
resolutiondisplay (default; simplified as a coverage, so neighboring zones keep their shared edges) / full (the full-resolution polygons the lookup uses).

Each zone’s properties include school and district labels, nces_school_id, nces_lea_id, and its provenance: method, accuracy_class, positional_rmse_m, boundary_vintage, currency_status, currency_note (what changed and when, for a zone whose source predates an adopted change), source_url, source_publisher, and retrieved_on. Every format carries the note that a zone is not an official enrollment determination: a top-level note in GeoJSON and json, a README.txt in the shapefile zip, and the GeoParquet file metadata.

curl "https://georgiacivicdata.org/api/v1/education/school_attendance_zones?district_code=761&zone_level=high"
GET/api/v1/education/school_attendance_zones/schema

Every field with its type and description, the meaning of each enum value, the coordinate reference system (OGC:CRS84: longitude, latitude), the map from each field to its 10-character shapefile field name, and the companion files, with the columns of the coverage table. A shapefile text field holds at most 254 bytes, so a longer value (a long boundary_vintage or source_url) is cut there and ends with ...; the other formats carry the full text, and currency_note always fits.

GET/api/v1/education/school_attendance_zones/coverage

One row per Georgia school district: its status (complete, partial, one_school_per_grade, or not_available), the status of each level (zoned, one_school_per_grade, partial, not_available, or served_by_other_district), the share of its land and 2020 population the zones cover, and why anything is missing. format=json (default) or geojson (with district outlines).

GET/api/v1/education/school_attendance_zones/lookup

Give lat and lon, or tract_geoid, plus an optional grade and year.

ParameterReturns
lat + lonThe zones containing the point, the point’s 2020 census tract and county, its district (code, name, Census id, NCES LEA id), a level_status for each school level, and a coverage_status with the reason. A zone whose source predates a boundary change that could not be applied adds a currency_warning that says what changed, by level.
tract_geoidEvery zone covering a 2020 census tract, with the share of the tract’s 2020 population in each (from the school_attendance_zone_tracts crosswalk dataset). When no zone covers the tract, a coverage_reason says why.
coverage_statusMeaning
zonedThe point is inside a zone at every school level its grades fall in.
gapThe district has zones, but at least one school level has none at this point (zones found at the other levels are still listed).
one_school_per_gradeThe district runs one school for each grade, so that school’s zone is the whole district.
not_availableThe district publishes no zones at one or more school levels the point’s grades fall in (zones found at the other levels are still listed); the reason says why.
not_zonedNo attendance zone in the district assigns the grade asked (pre-K, whose places are filled by application).
outside_georgiaThe point is outside Georgia.

Every response carries a note that it is not an official enrollment determination, and is sent with Cache-Control: no-store. A GET puts the coordinates in the URL, which passes through our hosting platform; our application never writes them to its logs. To keep a point out of URLs, send it by POST.

curl "https://georgiacivicdata.org/api/v1/education/school_attendance_zones/lookup?lat=33.7479&lon=-84.3903&grade=09"
POST/api/v1/education/school_attendance_zones/lookup

A JSON body with exactly one of the following, plus an optional grade and year:

BodyDescription
{"address": "..."}One address, geocoded by the U.S. Census Geocoder (benchmark Public_AR_Current, vintage Census2020_Current). The response adds the matched address, its coordinates, and the geocoder’s tract.
{"lat": .., "lon": ..}One point.
{"points": [{"id", "lat", "lon"}, ...]}Up to 1,000 points; each result carries its id.

Addresses are accepted only by POST, so they never appear in a URL. Our application never writes addresses or coordinates to its application logs, and never caches or records them. POST is also the private way to send a point. Responses are sent with Cache-Control: no-store. Rate limits per client: 120 requests a minute for GET lookups and 20 a minute for POST.

curl -X POST "https://georgiacivicdata.org/api/v1/education/school_attendance_zones/lookup" \
  -H "Content-Type: application/json" \
  -d '{"address": "55 Trinity Ave SW, Atlanta, GA 30303"}'
GET/api/v1/education/school_district_boundaries

Census TIGER/Line school district boundaries for Georgia, keyed to district_code. Filter with district_code; the same format options as the zones.