Protocol Spec
OGP Specification
Formal data model, JSON schemas, and validation rules.
Derived from the reference implementation. For conceptual explanations, see the docs.
Data Model
Country
Root entity. Primary key is the ISO 3166-1 alpha-3 code.
| field | type | constraint | description |
|---|---|---|---|
| alpha_3 | string(3) | PK | ISO 3166-1 alpha-3 — e.g. bra, usa, deu |
| alpha_2 | string(2) | ISO 3166-1 alpha-2 — e.g. br, us, de | |
| numeric | string(3) | ISO 3166-1 numeric — e.g. 076 | |
| flag | string(2) | Emoji flag string — e.g. 🇧🇷 | |
| short_name | string | Common name — e.g. Brazil | |
| official_name | string? | Official name — e.g. Federative Republic of Brazil | |
| id_for_osm | string | unique | OpenStreetMap relation ID |
| i18n | object | Internationalized names keyed by BCP 47 locale | |
| tags | object | Free-form metadata tags | |
| centroid | Point? | SRID 4326 | Geographic centroid |
| geom | MultiPolygon? | SRID 4326 | Country boundary polygon |
SubdivisionContext
Defines a geographic hierarchy scheme for a country. Multiple contexts per country are allowed; exactly one must be the default.
| field | type | constraint | description |
|---|---|---|---|
| id | string(64) | PK | Context ID — format: {COUNTRY}-{scheme} e.g. BRA-administrative |
| country | FK Country | The country this context belongs to | |
| name | string(64) | Human-readable name | |
| description | string(128) | Short description of the hierarchy scheme | |
| is_default | boolean | unique per country | True for exactly one context per country |
| i18n | object | Internationalized names |
SubdivisionType
Defines a level in the geographic hierarchy. The local_level field
is the ordinal position — higher = more granular.
| field | type | constraint | description |
|---|---|---|---|
| id | string(8) | PK | Slug — format: {country}-{type} e.g. bra-state, bra-neighborhood |
| country | FK Country | generated | Derived from first 3 chars of id |
| context | FK SubdivisionContext | The context this type belongs to | |
| slug | string(64) | Singular slug — e.g. estado, municipio, bairro | |
| slug_plural | string(64) | Plural slug — e.g. estados, municipios, bairros | |
| name | string(64) | Singular name — e.g. Estado, Município, Bairro | |
| name_plural | string(64) | Plural name | |
| local_level | integer | Ordinal position in hierarchy. 1 = country root. Higher = more granular. | |
| id_for_iso | integer? | ISO 3166-2 numeric type code | |
| osm_admin_level | integer? | Corresponding OSM admin_level tag value | |
| types_for_gmaps | object | Mapping from Google Maps address_component types to this SubdivisionType | |
| i18n | object | Internationalized names |
Subdivision
A single geographic subdivision — a neighborhood, municipality, state, or country. Geometry is stored separately in SubdivisionShape.
| field | type | constraint | description |
|---|---|---|---|
| id | bigint | PK | Internal auto-increment identifier |
| country | FK Country | generated | Derived from type.country |
| type | FK SubdivisionType | The type/level of this subdivision | |
| source | FK SubdivisionSource | Data source — e.g. IBGE, OSM, Brasil Aberto | |
| id_for_source | string(32)? | Source-specific identifier | |
| id_for_iso | string(6)? | ISO 3166-2 code — e.g. BR-SP | |
| id_for_osm | bigint? | OpenStreetMap relation ID | |
| id_for_gmaps | string(94)? | Google Maps place_id | |
| name | string(96) | Full name — e.g. São Paulo | |
| short_name | string(32)? | Short name — e.g. SP | |
| slug | string(96) | URL slug — e.g. sao-paulo | |
| short_slug | string(32)? | Short slug — e.g. sp | |
| parents | M2M Subdivision | through SubdivisionHierarchy | Parent subdivisions in the active context |
| children | M2M Subdivision | through SubdivisionHierarchy | Child subdivisions in the active context |
| i18n | object | Internationalized names | |
| tags | object | Free-form tags | |
| updated_at | datetime | auto | Last modified timestamp |
SubdivisionHierarchy
Many-to-many through table that stores parent-child relationships between subdivisions,
scoped by context. A subdivision can have multiple parents (e.g., a neighborhood straddling a district boundary).
Root nodes are identified by parent_id == child_id.
| field | type | constraint | description |
|---|---|---|---|
| id | bigint | PK | Auto-increment |
| country | FK Country | generated | Derived from context.country |
| context | FK SubdivisionContext | The hierarchy context this relationship belongs to | |
| parent | FK Subdivision | Parent subdivision | |
| child | FK Subdivision | Child subdivision | |
| is_root | boolean | generated | True when parent_id == child_id (self-reference = root node) |
Unique constraint: (parent, child, context)
SubdivisionShape
Geographic geometry for a subdivision. Stored separately to keep the Subdivision table lean.
Used for point-in-polygon reverse geocoding via PostGIS ST_Contains().
| field | type | constraint | description |
|---|---|---|---|
| subdivision | OneToOne Subdivision | PK/FK | The subdivision this shape belongs to |
| centroid | Point | SRID 4326 | Geographic centroid of the shape |
| geom | MultiPolygon | SRID 4326 | Full boundary polygon(s) |
| source | FK SubdivisionSource | Data source — e.g. IBGE Malhas, OSM Geofabrik |
SubdivisionPath
Denormalized path cache. Stores the canonical path string
for a (subdivision, context) pair.
This is the fast lookup key for all path-based API queries — path generation via hierarchy traversal is expensive,
so it's computed once and cached here.
| field | type | constraint | description |
|---|---|---|---|
| id | bigint | PK | Auto-increment |
| country | FK Country | generated | Derived from context.country |
| context | FK SubdivisionContext | The context this path belongs to | |
| subdivision | OneToOne Subdivision? | unique, db_constraint=False | The subdivision this path identifies |
| path | string(256) | unique | Full path — e.g. bra/sp/sao-paulo/tatuape |
| short_path | string(256)? | unique | Path without country prefix — e.g. sp/sao-paulo/tatuape |
| updated_at | datetime | auto | Last update timestamp |
Unique constraint: (subdivision, context)
API Schemas
GeoPath Response
Returned by all reverse geocoding endpoints and the main /{geopath} registry lookup.
{
"path": "bra/sp/sao-paulo/tatuape",
"hint": "internal",
"slug": "tatuape",
"name": "Tatuapé",
"recursive_title": "Tatuapé, bairro em São Paulo, SP, Brasil",
"ancestry_title": "bairro em São Paulo, SP",
"subdivision": {
"id": 42819,
"local_feed": null
},
"servers": [
{
"url": "https://findera.app/geo/bra/sp/sao-paulo/tatuape",
"type": "feed",
"operator": "findera.com.br"
}
],
"geoshape": {
"type": "MultiPolygon",
"coordinates": [...]
}
} {
"path": "bra/sp/sao-paulo/vila-prudente",
"hint": "external",
"slug": "vila-prudente",
"name": "Vila Prudente",
"recursive_title": "Vila Prudente, bairro em São Paulo, SP, Brasil",
"ancestry_title": "bairro em São Paulo, SP",
"subdivision": null
} | field | type | constraint | description |
|---|---|---|---|
| path | string | always | Canonical SubdivisionPath |
| hint | "internal" | "external" | always | Where the result originated. internal = found in registry DB. |
| slug | string | always | Last segment slug — e.g. tatuape |
| name | string | always | Human-readable name of the subdivision |
| recursive_title | string? | optional | Full hierarchical title — e.g. Tatuapé, bairro em São Paulo, SP, Brasil |
| ancestry_title | string? | optional | Ancestry description — e.g. bairro em São Paulo, SP |
| subdivision | object? | internal only | Present only when hint=internal. Contains subdivision id. |
| servers | array? | registry lookup only | List of registered content servers for this geopath. |
| geoshape | GeoJSON? | registry lookup only | Boundary geometry as GeoJSON MultiPolygon. |
Children Response
Returned by GET api.geofeed.ai/{geopath}/children.
Lists direct children within the active context.
{
"path": "bra/sp/sao-paulo",
"context": "BRA-administrative",
"count": 96,
"children": [
{
"path": "bra/sp/sao-paulo/tatuape",
"slug": "tatuape",
"name": "Tatuapé",
"type": "bra-neighborhood",
"local_level": 6
},
...
]
} Autocomplete Response
Returned by GET api.geofeed.ai/autocomplete.
Suggestions carry signed tokens — passing a token back resolves the selection without re-querying Google Maps.
{
"session": "a1b2c3d4e5f6",
"query": "tatuapé",
"count": 3,
"suggestions": [
{
"token": "eyJ...",
"text": "Tatuapé",
"secondary": "São Paulo, SP, Brasil",
"matches": [{ "offset": 0, "length": 7 }]
}
]
}
Tokens are TimestampSigned base64-encoded Google Maps place IDs.
They expire after GEO_AUTOCOMPLETE_MAX_AGE seconds.
Pass an expired token to get a fresh autocomplete flow.
Errors
All errors return a JSON body with an error_type field.
No subdivision found for the given path, point, or address.
Path string does not match the required format or country code is unknown.
Point string cannot be parsed as lat,lng coordinates.
The provided context does not match the country inferred from the path.
No default context exists for the country and none was provided.
Country code (alpha-2 or alpha-3) not found in pycountry.
Endpoint exists but is not yet implemented (e.g. /search).
Validation
Path Validation
Rules applied to every SubdivisionPath string before any lookup.
All path segments must be lowercase.
First segment must be 2–3 letters. Alpha-2 codes are normalized to alpha-3 before storage.
First segment must resolve to a known ISO 3166-1 country (pycountry lookup).
Segments after the country may contain lowercase letters, digits, hyphens, and underscores.
Paths must not end with a slash.
Double slashes (bra//sp) are invalid.
Context Rules
Rules for the context parameter.
Context IDs follow the pattern {COUNTRY}-{scheme}, e.g. BRA-administrative. The country segment is uppercase alpha-3.
If omitted, the default context for the country inferred from the path is used. Each country has exactly one default context.
The country inferred from the path's first segment must match the country in the context ID. Mismatch → invalid_context error.
If no default context exists for the inferred country, the context parameter becomes required. Omitting it → unknown_context error.