Chizumulu API
Chizumulu API turns the public football updates that the Northern Region Football Association (NRFA, Malawi) publishes into JSON you can build on.
Every post is kept exactly as it was observed. Structured data (goals, scores, fixtures, call-ups and so on) is derived from that post and sits next to the original text. When a post does not say something, the API does not say it either: the value is null.
Base URL
Section titled “Base URL”https://api.chizumulu.netEvery public route is a GET and returns JSON. There is no authentication: no API key, no signup, no token. Just send the request.
Your first request
Section titled “Your first request”Ask for the two newest messages with their structured data:
curl "https://api.chizumulu.net/v1/structured?limit=2"Response
{ "items": [ { "rawUpdateId": 5, "source": "whatsapp", "sourceMessageId": "3EB0A1F2C4D5E6F70005", "sourceServerId": null, "sourceTimestamp": "2026-09-19T15:24:20.000Z", "observationCount": 1, "latestObservation": { "id": 5, "observationId": "8f1c6a52-3b7e-4c1a-9d20-0a1b2c3d4e05", "receivedAt": "2026-09-19T15:24:22.805Z", "text": "Fulltime \n\nMafu Stars FC 3–1 Luviri FC \n\n#CINRFALeagueOne\n#NRFATransformingTheGame" }, "processing": { "state": "done", "method": "ai", "model": "deepseek/deepseek-v4-flash-0731", "postType": "result", "translationState": "done", "reusedFromObservationId": null, "error": null }, "structured": { "schemaVersion": 2, "postType": "result", "competition": { "code": "nrfa_league_one", "conflict": false, "evidence": [ { "code": "nrfa_league_one", "signal": "#CINRFALeagueOne", "kind": "hashtag" } ] }, "matches": [ { "index": 0, "homeTeam": "Mafu Stars FC", "awayTeam": "Luviri FC", "homeScore": 3, "awayScore": 1, "status": "full_time", "competition": null, "competitionCode": "nrfa_league_one", "competitionBasis": "post-signal", "venue": null, "dateText": null, "timeText": null, "roundText": null, "minuteText": null, "competitionGroupText": null, "evidence": { "text": true, "attachments": [] }, "events": [ { "index": 0, "type": "full_time", "minuteText": null, "team": null, "homeScore": null, "awayScore": null, "players": [] } ] } ], "facts": [] }, "korean": { "text": "풀타임\n\n마푸 스타즈 FC 3–1 루비리 FC\n\n#CINRFALeagueOne\n#NRFATransformingTheGame", "model": "deepseek/deepseek-v4-flash-0731" } }, { "rawUpdateId": 4, "source": "whatsapp", "sourceMessageId": "3EB0A1F2C4D5E6F70004", "sourceServerId": null, "sourceTimestamp": "2026-09-19T15:20:52.000Z", "observationCount": 2, "latestObservation": { "id": 6, "observationId": "8f1c6a52-3b7e-4c1a-9d20-0a1b2c3d4e06", "receivedAt": "2026-09-19T15:26:31.207Z", "text": "88' GOAL! \n\nNorman Ndlovu completes his brace as Mafu Stars extend their lead! \n\nMafu Stars FC 3–1 Luviri FC \n\n#CINRFALeagueOne\n#NRFATransformingTheGame" }, "processing": { "state": "done", "method": "ai", "model": "deepseek/deepseek-v4-flash-0731", "postType": "live_update", "translationState": "done", "reusedFromObservationId": null, "error": null }, "structured": { "schemaVersion": 2, "postType": "live_update", "competition": { "code": "nrfa_league_one", "conflict": false, "evidence": [ { "code": "nrfa_league_one", "signal": "#CINRFALeagueOne", "kind": "hashtag" } ] }, "matches": [ { "index": 0, "homeTeam": "Mafu Stars FC", "awayTeam": "Luviri FC", "homeScore": 3, "awayScore": 1, "status": "live", "competition": null, "competitionCode": "nrfa_league_one", "competitionBasis": "post-signal", "venue": null, "dateText": null, "timeText": null, "roundText": null, "minuteText": "88'", "competitionGroupText": null, "evidence": { "text": true, "attachments": [] }, "events": [ { "index": 0, "type": "goal", "minuteText": "88'", "team": "Mafu Stars FC", "homeScore": null, "awayScore": null, "players": [ { "name": "Norman Ndlovu", "role": "scorer" } ] } ] } ], "facts": [] }, "korean": { "text": "88' 골!\n\n노먼 은들로부가 멀티골을 완성하며 마푸 스타즈가 리드를 넓힙니다!\n\n마푸 스타즈 FC 3–1 루비리 FC\n\n#CINRFALeagueOne\n#NRFATransformingTheGame", "model": "deepseek/deepseek-v4-flash-0731" } } ], "nextCursor": "MjAyNi0wOS0xOVQxNToyMDo1Mi4wMDBafDQ="}Read it top to bottom:
rawUpdateIdidentifies the source message.latestObservation.textis the original text, untouched.processing.statetells you whether the post has been processed. Only when it isdoneisstructuredfilled in. See Processing states.structured.matches[]holds the matches found in the post, each withevents[]. Anything the post did not state isnull. See the structured model.koreanis a derived Korean translation of the post, ornull.nextCursorgets you the next page. See Pagination.
Which route do I want?
Section titled “Which route do I want?”| I want to… | Use |
|---|---|
| show current, structured football data | /v1/structured |
| see every edit of one post | /v1/structured/{rawUpdateId}/history |
| keep my own copy in sync, without missing anything | /v1/feed |
| browse or filter the raw posts | /v1/updates and /v1/latest |
| know whether the service is healthy | /v1/status |
EndpointsEvery route, its parameters and what it returns.
Raw vs structuredObservations, edits, history and processing states.
Structured modelMatches, events, facts and how unknown values work.
What is not guaranteedWhat the API does not infer or promise.
Machine-readable
Section titled “Machine-readable”- OpenAPI 3.1 (English):
https://api.chizumulu.net/openapi.json - For agents:
https://api.chizumulu.net/llms.txt