{"openapi":"3.0.3","info":{"title":"SukuuData API","description":"Structured, developer-friendly education data for Ghana: every school we know of, the\nSHS/SHTS/TVET placement register (categories, programmes, boarding), and the official CSSPS\nschool-selection rules as an API.\n\n**Authentication**: send your key in the `X-API-Key` header.\n\n**Errors**: every error response is `{ \"success\": false, \"error\": \"<message>\", \"code\": \"<CODE>\" }`.\nBranch on `code`; the message is for humans and may change.\n\n**Rate limits**: monthly, per key. Every response carries `X-RateLimit-Limit`,\n`X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds).","version":"1.1.0","contact":{"name":"SukuuData","url":"https://sukuudata.com","email":"hello@sukuudata.com"}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","name":"X-API-Key","in":"header"}},"schemas":{"Error":{"type":"object","required":["success","error","code"],"properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string","description":"Human-readable message"},"code":{"type":"string","description":"Stable machine-readable code, e.g. NOT_FOUND, UNKNOWN_REGION"}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"totalPages":{"type":"integer"}}},"Programme":{"type":"object","properties":{"code":{"type":"string","description":"GES programme code, e.g. 502 (General Science)"},"name":{"type":"string"},"kind":{"type":"string","enum":["GENERAL","TVET"]}}},"SecondaryProfile":{"type":"object","description":"Placement data from the GES register. Present only for public SHS/SHTS/TVET and pilot private schools.","properties":{"csspsCode":{"type":"string","description":"7-digit school code used on the CSSPS selection form"},"category":{"type":"string","enum":["A","B","C","PILOT_PRIVATE"],"description":"PILOT_PRIVATE: private senior high schools in the placement register (from 2026); chosen as day schools"},"institutionType":{"type":"string","enum":["SHS","SHTS","TVET"]},"gender":{"type":"string","enum":["MIXED","BOYS","GIRLS"]},"offersDay":{"type":"boolean"},"offersBoarding":{"type":"boolean"},"programmes":{"type":"array","items":{"$ref":"#/components/schemas/Programme"}},"specialNeeds":{"type":"array","items":{"type":"string","enum":["VISUALLY_IMPAIRED","HEARING_IMPAIRED","LEARNING_DIFFICULTIES"]},"description":"Special education facilities the school has"},"registerYear":{"type":"integer","description":"Year of the GES register this came from"},"locationConfidence":{"type":"string","enum":["HIGH","MEDIUM","NONE"],"description":"How sure we are that the coordinates belong to this school. Register schools are linked to mapped (OpenStreetMap) schools by name; NONE means we have no location for it yet."}}},"School":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"level":{"type":["null","string"],"enum":["KG","PRIMARY","JHS","SHS","TVET",null]},"type":{"type":["null","string"],"enum":["Public","Private",null],"description":"Public or private"},"gender":{"type":["null","string"],"enum":["MIXED","BOYS","GIRLS",null]},"residentialType":{"type":["null","string"],"enum":["DAY","BOARDING","MIXED",null]},"region":{"type":"string"},"regionCode":{"type":"string"},"district":{"type":"string"},"districtCode":{"type":"string"},"town":{"type":["null","string"]},"latitude":{"type":["null","number"]},"longitude":{"type":["null","number"]},"website":{"type":["null","string"]},"email":{"type":["null","string"]},"phone":{"type":["null","string"]},"status":{"type":"string","enum":["ACTIVE","NEEDS_REVIEW"]},"source":{"type":"string","description":"HOTOSM (OpenStreetMap) or GES_REGISTER"},"lastVerifiedAt":{"type":"string","format":"date-time"},"details":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"secondary":{"anyOf":[{"$ref":"#/components/schemas/SecondaryProfile"},{"type":"null"}]}}},"Region":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"code":{"type":"string","description":"Slug used in filters, e.g. greater-accra"},"pcode":{"type":["null","string"],"description":"OCHA admin pcode, e.g. GH07"},"schoolCount":{"type":"integer"}}},"District":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"code":{"type":"string"},"pcode":{"type":["null","string"]},"region":{"type":"string"},"regionCode":{"type":"string"},"schoolCount":{"type":"integer"}}}}},"paths":{"/api/v1/regions":{"get":{"summary":"List regions","tags":["Geography"],"description":"All 16 regions, with the number of active schools in each.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Region"}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/regions/{id}":{"get":{"summary":"Get a region","tags":["Geography"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Region id, code, pcode or name"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Region"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/districts":{"get":{"summary":"List districts","tags":["Geography"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"region","required":false,"description":"Only districts in this region (code, pcode or name)"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/District"}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/districts/{id}":{"get":{"summary":"Get a district","tags":["Geography"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"District id, code, pcode or name"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/District"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools":{"get":{"summary":"List and filter schools","tags":["Schools"],"description":"Paginated list of schools. Filters combine with AND; comma-separated values within one filter combine with OR.","parameters":[{"schema":{"type":"string"},"example":"greater-accra","in":"query","name":"region","required":false,"description":"Region code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"district","required":false,"description":"District code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"example":"SHS","in":"query","name":"level","required":false,"description":"KG, PRIMARY, JHS, SHS or TVET. Comma-separate to match any of several values."},{"schema":{"type":"string","deprecated":true},"in":"query","name":"type","required":false,"description":"Deprecated alias of level."},{"schema":{"type":"string"},"in":"query","name":"schoolType","required":false,"description":"PUBLIC or PRIVATE. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"gender","required":false,"description":"School gender: MIXED, BOYS or GIRLS. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"residential","required":false,"description":"DAY, BOARDING or MIXED (day and boarding). Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"ACTIVE (default), NEEDS_REVIEW (no longer in the source; may have closed) or ALL. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"town","required":false,"description":"Exact town name (case-insensitive)."},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Substring of the school name (case-insensitive)."},{"schema":{"type":"boolean"},"in":"query","name":"hasLocation","required":false,"description":"Only schools with (true) or without (false) coordinates."},{"schema":{"type":"string"},"in":"query","name":"sort","required":false,"description":"name (default) or lastVerifiedAt; prefix with - for descending."},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/School"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools/search":{"get":{"summary":"Autocomplete schools by name","tags":["Schools"],"description":"Typo-tolerant name search for school pickers and sign-up forms. Prefix matches rank first. Returns at most `limit` results, best first.","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":100},"in":"query","name":"q","required":true,"description":"What the user has typed so far"},{"schema":{"type":"string"},"in":"query","name":"region","required":false,"description":"Region code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"level","required":false,"description":"KG, PRIMARY, JHS, SHS or TVET. Comma-separate to match any of several values."},{"schema":{"type":"integer","minimum":1,"maximum":25,"default":10},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/School"}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools/nearby":{"get":{"summary":"Schools near a point","tags":["Schools"],"description":"Schools within `radius` km of a point, nearest first. Accepts the same filters as GET /schools.","parameters":[{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"lat","required":true,"description":"Latitude (WGS84)"},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"lng","required":true,"description":"Longitude (WGS84)"},{"schema":{"type":"number","exclusiveMinimum":0,"maximum":50,"default":5},"in":"query","name":"radius","required":false,"description":"Radius in km (max 50)"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"example":"greater-accra","in":"query","name":"region","required":false,"description":"Region code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"district","required":false,"description":"District code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"example":"SHS","in":"query","name":"level","required":false,"description":"KG, PRIMARY, JHS, SHS or TVET. Comma-separate to match any of several values."},{"schema":{"type":"string","deprecated":true},"in":"query","name":"type","required":false,"description":"Deprecated alias of level."},{"schema":{"type":"string"},"in":"query","name":"schoolType","required":false,"description":"PUBLIC or PRIVATE. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"gender","required":false,"description":"School gender: MIXED, BOYS or GIRLS. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"residential","required":false,"description":"DAY, BOARDING or MIXED (day and boarding). Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"ACTIVE (default), NEEDS_REVIEW (no longer in the source; may have closed) or ALL. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"town","required":false,"description":"Exact town name (case-insensitive)."},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Substring of the school name (case-insensitive)."},{"schema":{"type":"boolean"},"in":"query","name":"hasLocation","required":false,"description":"Only schools with (true) or without (false) coordinates."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/School"},{"type":"object","properties":{"distanceKm":{"type":"number","description":"Straight-line distance"}}}]}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools/{id}":{"get":{"summary":"Get a school","tags":["Schools"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/School"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools/{id}/provenance":{"get":{"summary":"Get field-level school provenance","tags":["Data"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"School id, code, pcode or name"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schools/{id}/corrections":{"post":{"summary":"Suggest a school-data correction","tags":["Data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["field","proposedValue"],"additionalProperties":false,"properties":{"field":{"type":"string","minLength":1,"maxLength":50},"proposedValue":{"type":"string","minLength":1,"maxLength":1000},"evidenceUrl":{"type":"string","format":"uri"},"note":{"type":"string","maxLength":2000},"reporterEmail":{"type":"string","format":"email"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"School id, code, pcode or name"}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/secondary-schools":{"get":{"summary":"Find SHS, SHTS and TVET schools in the placement register","tags":["Secondary schools"],"description":"Schools in the GES placement register, with category, programmes and boarding. Pass `lat`/`lng` to limit to a radius and get distances (sorted nearest first by default). Use `studentGender` to get only schools the student can attend.","parameters":[{"schema":{"type":"string"},"in":"query","name":"region","required":false,"description":"Region code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"district","required":false,"description":"District code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Substring of the school name"},{"schema":{"type":"string"},"example":"A,B","in":"query","name":"category","required":false,"description":"Placement category: A, B, C or PILOT_PRIVATE. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"institutionType","required":false,"description":"SHS, SHTS (senior high/technical) or TVET. Comma-separate to match any of several values."},{"schema":{"type":"string"},"example":"502","in":"query","name":"programme","required":false,"description":"Programme code the school must offer (see GET /programmes). Comma-separate to match any of several values."},{"schema":{"type":"string","enum":["MALE","FEMALE","male","female"]},"in":"query","name":"studentGender","required":false,"description":"Only schools open to this student"},{"schema":{"type":"string"},"in":"query","name":"gender","required":false,"description":"School gender: MIXED, BOYS or GIRLS. Ignored when studentGender is set. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"residential","required":false,"description":"DAY or BOARDING: only schools offering it. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"specialNeeds","required":false,"description":"VISUALLY_IMPAIRED, HEARING_IMPAIRED or LEARNING_DIFFICULTIES: schools with these facilities. Comma-separate to match any of several values."},{"schema":{"type":"boolean"},"in":"query","name":"hasLocation","required":false},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"lat","required":false,"description":"Latitude (WGS84)"},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"lng","required":false,"description":"Longitude (WGS84)"},{"schema":{"type":"number","exclusiveMinimum":0,"maximum":300,"default":25},"in":"query","name":"radius","required":false,"description":"km, when lat/lng given"},{"schema":{"type":"string"},"in":"query","name":"sort","required":false,"description":"name, -name, category, -category, or distance (needs lat/lng)"},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/School"},{"type":"object","properties":{"distanceKm":{"type":"number","description":"Straight-line distance"}}}]}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/secondary-schools/{code}":{"get":{"summary":"Get a secondary school by CSSPS code","tags":["Secondary schools"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9]{7}$"},"in":"path","name":"code","required":true,"description":"7-digit CSSPS school code"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/School"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/secondary-schools/{code}/history":{"get":{"summary":"Get a school's annual register history","tags":["Secondary schools"],"parameters":[{"schema":{"type":"string","pattern":"^[0-9]{7}$"},"in":"path","name":"code","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/programmes":{"get":{"summary":"List programmes","tags":["Secondary schools"],"description":"General programmes (e.g. 502 General Science) and TVET trades, with how many schools offer each.","parameters":[{"schema":{"type":"string"},"in":"query","name":"kind","required":false,"description":"GENERAL or TVET"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Programme"},{"type":"object","properties":{"schoolCount":{"type":"integer"}}}]}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/placement/rules":{"get":{"summary":"CSSPS school-selection rules (2026)","tags":["Placement"],"description":"The official rules as data, with their source, so apps can show and enforce them.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","additionalProperties":true,"properties":{"year":{"type":"integer"},"source":{"type":"object","additionalProperties":true,"properties":{}},"totalChoices":{"type":"integer"},"maxBoardingChoices":{"type":"integer"},"maxDayChoices":{"type":"integer"},"categoryLimits":{"type":"object","additionalProperties":true,"properties":{}},"noRepeatedSchools":{"type":"boolean"},"dayCatchmentKm":{"type":"number"},"notes":{"type":"array","items":{"type":"string"}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/placement/validate":{"post":{"summary":"Validate a candidate's school choices","tags":["Placement"],"description":"Checks a BECE candidate's CSSPS choices against the official rules (8 choices, boarding/day split, category limits, no repeats) and against what each school offers (programme, boarding, gender). `errors` break a rule; `warnings` are advice, such as a day school far from home. Returns 200 with `valid: false` for a form that breaks rules; 400 only for a malformed request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["choices"],"additionalProperties":false,"properties":{"studentGender":{"type":"string","enum":["MALE","FEMALE"]},"home":{"type":"object","required":["lat","lng"],"additionalProperties":false,"properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude (WGS84)"},"lng":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude (WGS84)"}},"description":"Candidate's home, to check day schools are within reach"},"choices":{"type":"array","minItems":1,"maxItems":20,"description":"In order of preference","items":{"type":"object","required":["programme","residential"],"additionalProperties":false,"oneOf":[{"required":["csspsCode"]},{"required":["schoolId"]}],"properties":{"csspsCode":{"type":"string","pattern":"^[0-9]{7}$"},"schoolId":{"type":"string"},"programme":{"type":"string","description":"Programme code, e.g. 502"},"residential":{"type":"string","enum":["DAY","BOARDING"]}}}}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"valid":{"type":"boolean"},"rulesYear":{"type":"integer"},"registerYear":{"type":["null","integer"]},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"e.g. CATEGORY_A_LIMIT, PROGRAMME_NOT_OFFERED, DAY_SCHOOL_FAR"},"message":{"type":"string"},"choice":{"type":"integer","description":"1-based choice the issue is about, if any"}}}},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"e.g. CATEGORY_A_LIMIT, PROGRAMME_NOT_OFFERED, DAY_SCHOOL_FAR"},"message":{"type":"string"},"choice":{"type":"integer","description":"1-based choice the issue is about, if any"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer"},"boarding":{"type":"integer"},"day":{"type":"integer"},"byCategory":{"type":"object","additionalProperties":{"type":"object","properties":{"total":{"type":"integer"},"boarding":{"type":"integer"},"day":{"type":"integer"}}}}}},"choices":{"type":"array","items":{"type":"object","properties":{"choice":{"type":"integer"},"school":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"csspsCode":{"type":"string"},"category":{"type":"string"},"region":{"type":"string"},"district":{"type":"string"}}},{"type":"null"}]},"programme":{"anyOf":[{"$ref":"#/components/schemas/Programme"},{"type":"null"}]},"residential":{"type":"string"},"distanceKm":{"type":["null","number"]}}}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/placement/recommend":{"post":{"summary":"Recommend eligible schools","tags":["Placement"],"description":"Scores schools against programme preferences, gender, residential preference and location; explains every score and proposes a CSSPS-rule-aware eight-choice set.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["programmes"],"additionalProperties":false,"properties":{"programmes":{"type":"array","minItems":1,"maxItems":10,"uniqueItems":true,"items":{"type":"string"}},"studentGender":{"type":"string","enum":["MALE","FEMALE"]},"residential":{"type":"string","enum":["DAY","BOARDING"]},"region":{"type":"string"},"district":{"type":"string"},"category":{"type":"string"},"institutionType":{"type":"string"},"specialNeeds":{"type":"string"},"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude (WGS84)"},"lng":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude (WGS84)"},"radius":{"type":"number","exclusiveMinimum":0,"maximum":300},"limit":{"type":"integer","minimum":1,"maximum":25,"default":10}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/statistics/schools":{"get":{"summary":"School statistics","tags":["Data"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"region","required":false,"description":"Region code, pcode or name"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"region":{"type":"string"},"total":{"type":"integer"},"public":{"type":"integer"},"private":{"type":"integer"},"withLocation":{"type":"integer"},"byLevel":{"type":"object","additionalProperties":{"type":"integer"}},"placementSchoolsByCategory":{"type":"object","additionalProperties":{"type":"integer"}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/data-quality":{"get":{"summary":"Data sources and completeness","tags":["Data"],"description":"Where the data comes from (with licences), when it was last synced, and what share of schools have each field. Check this before relying on a field.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"schools":{"type":"object","properties":{"active":{"type":"integer"},"needsReview":{"type":"integer"},"completeness":{"type":"object","additionalProperties":{"type":"object","properties":{"count":{"type":"integer"},"percent":{"type":"number"}}}}}},"placementRegister":{"type":"object","properties":{"schools":{"type":"integer"},"year":{"type":["null","integer"]},"withLocation":{"type":"integer"},"rulesYear":{"type":"integer"},"outdated":{"type":"boolean"}}},"sources":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":["null","string"]},"licence":{"type":["null","string"]},"records":{"type":"integer"},"dataVersion":{"type":["null","string"]},"lastSyncedAt":{"type":["null","string"],"format":"date-time"}}}}}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/changes":{"get":{"summary":"List dataset changes","tags":["Data"],"parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"since","required":false},{"schema":{"type":"string"},"in":"query","name":"entityType","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/exports/schools.{format}":{"get":{"summary":"Export filtered schools as CSV or GeoJSON","tags":["Data"],"parameters":[{"schema":{"type":"string"},"example":"greater-accra","in":"query","name":"region","required":false,"description":"Region code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"district","required":false,"description":"District code, pcode or name. Comma-separate to match any of several values."},{"schema":{"type":"string"},"example":"SHS","in":"query","name":"level","required":false,"description":"KG, PRIMARY, JHS, SHS or TVET. Comma-separate to match any of several values."},{"schema":{"type":"string","deprecated":true},"in":"query","name":"type","required":false,"description":"Deprecated alias of level."},{"schema":{"type":"string"},"in":"query","name":"schoolType","required":false,"description":"PUBLIC or PRIVATE. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"gender","required":false,"description":"School gender: MIXED, BOYS or GIRLS. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"residential","required":false,"description":"DAY, BOARDING or MIXED (day and boarding). Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"ACTIVE (default), NEEDS_REVIEW (no longer in the source; may have closed) or ALL. Comma-separate to match any of several values."},{"schema":{"type":"string"},"in":"query","name":"town","required":false,"description":"Exact town name (case-insensitive)."},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Substring of the school name (case-insensitive)."},{"schema":{"type":"boolean"},"in":"query","name":"hasLocation","required":false,"description":"Only schools with (true) or without (false) coordinates."},{"schema":{"type":"string","enum":["csv","geojson"]},"in":"path","name":"format","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/v1/webhooks":{"get":{"summary":"List webhooks","tags":["Developers"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a signed change webhook","tags":["Developers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"additionalProperties":false,"properties":{"url":{"type":"string","format":"uri","pattern":"^https://"},"events":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["school.created","school.updated","school.review","register.updated","correction.approved"]}}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}":{"delete":{"summary":"Delete a webhook","tags":["Developers"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Webhook id, code, pcode or name"}],"responses":{"204":{"description":"Default Response"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Monthly quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"servers":[{"url":"https://api.sukuudata.com","description":"Production"},{"url":"/","description":"This server"}],"security":[{"apiKey":[]}],"tags":[{"name":"Schools","description":"Every school we have a record for, KG to TVET"},{"name":"Secondary schools","description":"SHS/SHTS/TVET and pilot private schools from the GES placement register"},{"name":"Placement","description":"CSSPS school-selection rules and choice validation"},{"name":"Geography","description":"Regions and districts"},{"name":"Data","description":"Statistics, sources and data quality"},{"name":"Developers","description":"Webhooks and developer operations"}],"externalDocs":{"description":"Get a free API key","url":"https://sukuudata.com/register"}}