Data Standardization SOP
This page is the source of truth for how the Public Data API spells names and notations. Use it when you $filter institutions or companies, and when you map a new national dataset into the catalog.
The rules below match what the public API already documents and what new imports are required to emit. They do not change historical rows by themselves.
The usual equals operator on address fields is an exact string match. Spell country and state values as this SOP specifies — for example United States and California, not US or CA. Those fields also support starts with and contains.
Source of Truth
Resolve values in this order:
- Public API contract — the English strings integrators already filter on, as shown in Getting Started and the list-endpoint samples.
- ISO 3166-1 alpha-2 — the code key for a country (flags, coverage tables, national identifier prefixes).
- English CLDR short names —
new Intl.DisplayNames(['en'], { type: 'region' }).of(code)is how we turn that code into the storedaddresses.countryvalue. The coverage table is generated from the same function. - Closed enums in
@epiphany/typesfor institutiontypeand addresstype. Do not invent new values.
Dashboard geography pickers (apps/dashboard/src/constants.js) are a separate UI catalog. Do not copy those labels into Public Data addresses when they disagree with CLDR (for example dashboard China vs coverage China mainland).
Country Names
addresses.country stores the English CLDR short name, not an ISO code.
| Role | Format | Examples |
|---|---|---|
Stored / filterable addresses.country | English CLDR short name | United States, United Kingdom, Czechia, Türkiye |
| Code key (flags, coverage, identifier prefixes) | ISO 3166-1 alpha-2 | US, GB, CZ, TR |
| Flag asset | Lowercase alpha-2 | https://ed.link/flags/us.svg |
Do not store USA, United States of America, UK, Great Britain, Holland, or Czech Republic as addresses.country.
How new imports must map countries
- Trim the source value.
- If it is already a two-letter code, treat it as ISO 3166-1 alpha-2 (uppercase for
Intl.DisplayNames, which is unreliable with lowercase codes). - Apply the aliases we already use in coverage generation:
UK→GB,FX→FR. - Otherwise look the name up in a source-specific name→ISO map (the EQAR importer in
utils/entities/countries/eqar/eqar.tsis the existing example), then expand withIntl.DisplayNames. - Write the English display name into the CSV
countrycolumn and intoentities.institution_addresses.country/entities.company_addresses.country. Do not leave the alpha-2 code in that field.
Common expansions:
| Source value | ISO 3166-1 alpha-2 | Stored addresses.country |
|---|---|---|
US, USA, United States of America | US | United States |
UK, GB, Great Britain, Britain | GB | United Kingdom |
Czech Republic | CZ | Czechia |
Holland | NL | Netherlands |
Korea, South, Republic of Korea | KR | South Korea |
China, People's Republic of | CN | China mainland |
Cote d'Ivoire, Ivory Coast | CI | Côte d’Ivoire |
Holy See | VA | Vatican City |
Bosnia and Herzegovina | BA | Bosnia & Herzegovina |
The coverage table is the published spelling list for every flag we ship. If you add a country, regenerate that page with utils/entities/_gen-coverage-tables.mjs so the public name stays in lockstep with CLDR.
Filtering by country
const filter = {
'addresses.country': [{ operator: 'equals', value: 'United States' }]
};
Use the same English name you see on the coverage table.
States, Regions, and Provinces
addresses.state stores the full region name, not a postal abbreviation.
| Avoid | Store |
|---|---|
CA | California |
TX | Texas |
NSW | New South Wales |
ACT | Australian Capital Territory |
Rules:
- Prefer the official English name, or the source’s Latin-script name when that is the national standard (for example French
Île-de-France, ItalianLazio, SwedishStockholm County). - If the source only has a code, expand it with a national mapping table in the importer. Australia already does this (
ACT→Australian Capital Territory, and so on). Do not persist the abbreviation. - Use the administrative unit the source treats as the region: US state, Canadian province, French région, UK government office region or county, Chilean región, Singapore zone, and similar. Do not invent a second geography field.
- Leave
stateempty when the source has no region, as EQAR currently does.
const filter = {
'addresses.state': [{ operator: 'equals', value: 'California' }],
'addresses.country': [{ operator: 'equals', value: 'United States' }]
};
Cities, Postal Codes, and Coordinates
- City: Latin characters when a reasonable transliteration exists (
Tokyo, not東京都). Use proper case, notAUSTIN. - Postal code: Trim whitespace; keep the national format (do not strip spaces from UK postcodes or hyphens from US ZIP+4).
- Coordinates: WGS 84 decimal degrees in
latitude/longitude.
Institution Types
type is the closed InstitutionType enum. Unknown source types become other. Do not add new enum members from an importer.
| Value | Use for |
|---|---|
school | Primary and secondary establishments |
district | Local authorities, academy trusts, federations, education regions, académies, secretarías, and similar mid-level governing bodies |
college | Further education, polytechnics, junior colleges, community colleges, Fachhochschulen |
university | Universities and most EQAR higher-education records |
lea | An explicit local education agency when the source calls it that. Prefer district for generic local governing bodies |
government | National ministries and national education agencies |
organization | Non-school operators such as Chilean sostenedores |
other | Anything that does not fit the rows above (for example orientation services, special cases) |
Companies do not have a type field.
Address Types
addresses.type is the closed AddressType enum: physical, mailing, post_office_box, billing, shipping, headquarters, branch, other.
New institution imports default to physical. Company seeds default to headquarters. Only use another value when the source clearly distinguishes it.
Names, Descriptors, and Websites
These rules apply to institution and company name, descriptor, and website.
- Websites start with
https://. A school subpage is acceptable when that is the school’s public URL. - Case: Correct
ALL CAPSsource names to normal title case. - Abbreviations in names: Prefer letters without periods or spaces (
ISD, notI.S.D.). Do not leave common district abbreviations in the name — expandISD,CSD,CISD,USD, and similar. Keep periods when they are part of a personal name (H.C. Crittenden Middle School). - Irish / Scottish (and similar) expansions:
Scoil Náisiúntainstead ofS N/Scoil Nais;Boys National School/Girls National Schoolinstead ofB N S/G N S;Infant Boys/Infant Girlsinstead ofI B/I G. - Script: Prefer Latin characters for cities, states, and countries. Institution names may stay in the source script when there is no reasonable transliteration.
- Generic names: If the name is only a number or a generic label (
Ecole élémentaire), add a distinguishing place or official qualifier. - Descriptor: At most three words. Keep it general (
High School,Private School,National governing body). It appears in search results to disambiguate records. Do not write long source dumps intodescriptor.
Relationships
Parent/child rows use relationship_type governing_body. That is the only relationship type current Public Data importers write. Leave it empty when there is no parent.
Seed parents before children. When several institutions share a parent name, the CSV seeder prefers a government, district, or lea parent over a school.
Identifiers
Identifier type names are stored on entities.institution_identifier_types.name. They are not currently returned on the public institution or company objects; they are how we dedupe imports (identifier_type|identifier_value).
Convention for new national IDs: {ISO2}_{SOURCE_KEY} in SCREAMING_SNAKE (for example FR_UAI, IE_ROLL_NUMBER). Existing exceptions to keep:
| Type name | Country / source |
|---|---|
NCES, MDR, IPEDS | United States |
UK_GIAS_URN | United Kingdom (uses UK_, not GB_) |
EQAR_DEQAR_ID | EQAR higher education |
AU_ACARA_SML_ID | Australia |
CL_RBD, CL_RUT_SOSTENEDOR | Chile |
CO_DUE_DANE, CO_MEN_IES | Colombia |
ES_RUCT_UNIVERSITY_CODE | Spain |
FI_OPPILAITOSTUNNUS | Finland |
IT_CODICE_SCUOLA, IT_CODICE_ISTITUTO | Italy |
NL_BRIN_INSTELLINGSCODE, NL_BEVOEGD_GEZAG_NUMMER | Netherlands |
NZ_MOE_SCHOOL, NZ_MOE_ECE, NZ_MOE_TERTIARY | New Zealand |
PE_ESCALE_COD_MOD | Peru |
SE_SKOLENHETSKOD, SE_ORGANIZATION_NUMBER | Sweden |
SG_MOE_SCHOOL | Singapore |
ZA_EMIS_NUMBER | South Africa |
Reuse an existing type name when the same national register is imported again. Do not create phone identifier types.
Import Checklist
Before a new source lands in utils/entities:
- Map country → ISO 3166-1 alpha-2 → English CLDR name. Write the name, not the code.
- Expand state/province abbreviations to full names.
- Map each row to one
InstitutionTypevalue from the table above. - Normalize website, name, and descriptor as specified.
- Assign a stable
identifier_type/identifier_valuepair and asource_datasetslug (schools,governing_body,eqar, …). - Emit
governing_bodyrelationships with parent rows first. - Update coverage when the country or sector is new.
