구조화 모델 (schema v2)
structured는 observation 하나에서 파생된 결과입니다. structured.schemaVersion은 2입니다.
structured├─ schemaVersion 2├─ postType what kind of post this is├─ competition { code, conflict, evidence } post-level classification├─ matches[] 0..N matches, each with events[]└─ facts[] 0..N facts that are not matches (call-ups, statistics, standings, notices, transfers, community)게시물을 억지로 경기에 끼워 맞추지 않습니다. 생일 축하글은 matches: []이고 facts: [...](또는 둘 다 비어 있음)이며, 그것이 정상적인 결과입니다.
모르면 null
섹션 제목: “모르면 null”모든 필드 뒤에 있는 단 하나의 규칙: 게시물이 말하지 않았다면 값은 null입니다. API는 스코어, 팀, 분, 날짜, 대회를 추측하지 않으며 빈칸을 “그럴듯하게” 채우지도 않습니다.
null은 원본이 밝히지 않았다는 뜻입니다.0도 빈 문자열도 아닙니다.- 값은 적힌 그대로 복사됩니다. 철자, 대소문자, 문장부호, 날짜(
SATURDAY 19-09-2026), 분(45+2',88')은 정규화하거나 고치지 않습니다. 원본이 스스로 모순되거나 오타가 있으면 그것도 그대로 보존됩니다. (실제 예: 다른 게시물은M'mbelwa Warriors라고 쓰는데 어떤 게시물이M'mberwa Warriors라고 썼다면 적힌 그대로 반환됩니다.) - 종류를 나타내는 필드(
status, 이벤트type,postType,competitionCode)는 아래에 정리한 고정된 값 중 하나이거나null입니다.
postType
섹션 제목: “postType”| 값 | 의미 |
|---|---|
result |
끝난 경기: “Results” 목록, “Full time” 보고, 최종 스코어 요약. |
live_update |
진행 중인 경기: 킥오프, 골, 하프타임, 교체, 찬스, 분 단위 중계. |
fixtures |
아직 결과가 없는 예정 경기. |
match_update |
위에 해당하지 않는 경기 소식(예: 연기). |
standings |
리그 순위표나 순위. |
player_news |
선수에 관한 소식(소집, 이적, 기록). |
administrative |
협회나 클럽의 공지. |
promotional |
예고, 링크, 행사 홍보. |
community |
생일, 축하 같은 인사. |
other |
그 밖의 모든 것. |
경기(Matches)
섹션 제목: “경기(Matches)”matches[]에는 언급된 경기마다 항목 하나가 들어갑니다. 결과 게시물은 열 경기를 담을 수 있습니다. 한 게시물 안에서 같은 경기에 대한 여러 갱신은 항목 하나입니다.
| 필드 | 타입 | 의미 |
|---|---|---|
index |
integer | 이 observation 안에서의 위치. 전역 경기 식별자가 아닙니다. |
homeTeam, awayTeam |
string 또는 null |
A vs B, A 🆚 B, A 1-0 B에서는 왼쪽 팀이 홈입니다. A host B는 A가 홈입니다. 원본이 홈/원정을 밝히지 않으면(예: A will face B) 둘 다 null입니다. |
homeScore, awayScore |
integer 또는 null |
그 경기에 대해 명시된 스코어만. “8 appearances” 같은 기록은 스코어가 아닙니다. |
status |
scheduled, live, half_time, full_time, postponed, cancelled 또는 null |
게시물이 밝히거나 분명히 시사하는 상태: “Results” 제목이면 full_time, “Fixtures” 제목이면 scheduled. |
minuteText |
string 또는 null |
게시물이 이 시점에 대해 밝힌 경기 시계를 그대로(7 MINUTES, 43'). 없거나 서로 다른 시계가 여러 개 적혀 있으면 null(그때는 각 이벤트가 자기 분을 가집니다). |
competition |
string 또는 null |
아래 참고. |
competitionCode, competitionBasis |
아래 참고 | 결정적 분류. |
venue, dateText, timeText, roundText |
string 또는 null |
적힌 그대로(Wovwe Ground, SATURDAY 19-09-2026, Week 4). |
competitionGroupText |
string 또는 null |
경기가 속한 클러스터/그룹 제목을 적힌 그대로, 앞의 # 없이(CLUSTER A). |
evidence |
{ text, attachments } |
항목을 읽어 낸 곳: 게시물 텍스트, 그리고/또는 첨부 이미지의 0부터 시작하는 인덱스. |
events |
array | 아래 참고. |
curl "https://api.chizumulu.net/v1/structured?limit=2&cursor=MjAyNi0wOS0xOVQwNDo0ODoxOC4wMDBafDI="클러스터가 있는 경기 일정 게시물 (메시지 1)
{ "items": [ { "rawUpdateId": 1, "source": "whatsapp", "sourceMessageId": "3EB0A1F2C4D5E6F70001", "sourceServerId": null, "sourceTimestamp": "2026-09-19T04:47:30.000Z", "observationCount": 1, "latestObservation": { "id": 1, "observationId": "8f1c6a52-3b7e-4c1a-9d20-0a1b2c3d4e01", "receivedAt": "2026-09-19T04:47:32.412Z", "text": "Matchday live \n\n#NRFA LEAGUE 2\n#Week 4 Fixtures\n\n#SATURDAY 19-09-2026\n\n#CLUSTER A \nWovwe Vision 🆚 AirSport FC \nWovwe Ground \nMphompha Utd 🆚 Mhuju FC \nMphompha Ground \n\n#NRFADivisionLeague2\n#NRFATransformingTheGame" }, "processing": { "state": "done", "method": "deterministic", "model": null, "postType": "fixtures", "translationState": "none", "reusedFromObservationId": null, "error": null }, "structured": { "schemaVersion": 2, "postType": "fixtures", "competition": { "code": "nrfa_league_two", "conflict": false, "evidence": [ { "code": "nrfa_league_two", "signal": "#NRFA LEAGUE 2", "kind": "hashtag" }, { "code": "nrfa_league_two", "signal": "#NRFADivisionLeague2", "kind": "hashtag" } ] }, "matches": [ { "index": 0, "homeTeam": "Wovwe Vision", "awayTeam": "AirSport FC", "homeScore": null, "awayScore": null, "status": "scheduled", "competition": "NRFA LEAGUE 2", "competitionCode": "nrfa_league_two", "competitionBasis": "match-text", "venue": "Wovwe Ground", "dateText": "SATURDAY 19-09-2026", "timeText": null, "roundText": "Week 4", "minuteText": null, "competitionGroupText": "CLUSTER A", "evidence": { "text": true, "attachments": [] }, "events": [] }, { "index": 1, "homeTeam": "Mphompha Utd", "awayTeam": "Mhuju FC", "homeScore": null, "awayScore": null, "status": "scheduled", "competition": "NRFA LEAGUE 2", "competitionCode": "nrfa_league_two", "competitionBasis": "match-text", "venue": "Mphompha Ground", "dateText": "SATURDAY 19-09-2026", "timeText": null, "roundText": "Week 4", "minuteText": null, "competitionGroupText": "CLUSTER A", "evidence": { "text": true, "attachments": [] }, "events": [] } ], "facts": [] }, "korean": null } ], "nextCursor": null}경기 이벤트(Match events)
섹션 제목: “경기 이벤트(Match events)”events[]에는 게시물이 경기에서 일어났다고 보고한 일이 들어갑니다. 점유율이나 일반적인 경기 흐름을 묘사하기만 하는 게시물은 이벤트가 없어야 하며, 시계만 있는 갱신(43' 뒤에 중계 문장)은 이벤트 없이 시계를 minuteText에 보관합니다.
| 필드 | 타입 | 의미 |
|---|---|---|
index |
integer | 경기 안에서의 위치. |
type |
kickoff, goal, own_goal, penalty, half_time, second_half_kickoff, full_time, substitution, yellow_card, red_card, chance, save, free_kick, corner, other |
other는 나머지 어디에도 맞지 않는 실제 경기 내 사건에만 씁니다. |
minuteText |
string 또는 null |
적힌 그대로. |
team |
string 또는 null |
원본에 적힌 그대로의 팀. |
homeScore, awayScore |
integer 또는 null |
그 시점에 명시된 스코어이며, 게시물이 해당 이벤트에 대해 스코어를 밝힌 경우에만. |
players |
{ name, role } 배열 |
role은 scorer, assist, build_up, player_in, player_out, carded, taker, keeper, involved, coach, referee, official, other 중 하나이거나 null. |
관례상 한 경기에 대한 명시적인 “Full time” 줄은 상태 full_time과 full_time 이벤트 하나를 만들고, 일반적인 “Results” 목록은 경기마다 full_time 이벤트 없이 각 경기를 full_time으로 만듭니다. full_time 이벤트는 선택적인 것으로 보고 status에 의존하세요.
팩트(Facts)
섹션 제목: “팩트(Facts)”facts[]에는 경기가 아닌 것이 들어갑니다. 각 항목은 { index, type, data, evidence }입니다. type은 아래 값 중 하나이며 data의 키를 결정합니다.
type |
data의 키 |
|---|---|
call_up |
player, club, position, called_to, squad_text, competition, opponent, first_leg_date_text, camp_date_text |
player_stats |
player, club, competition, stats ({ name, value } 배열. name은 숫자를 가리키는 게시물 속 단어 그대로) |
standings |
competition, round_text, date_text, season_text, rows ({ position, team, played, won, drawn, lost, goal_difference, points } 배열) |
notice |
notice_type (club_rename, registration, disciplinary, schedule_change, venue_change, event_notice, other), subject, old_value, new_value, date_text |
transfer |
player, from_club, to_club, position |
community |
occasion (birthday, congratulations, condolence, thanks, event_promo, other 또는 null), recipients (문자열 배열) |
이미지에서 읽은 사실
섹션 제목: “이미지에서 읽은 사실”보관된 이미지에서 읽을 수 있는 것은 한 가지, 리그 순위표입니다. 이는 게시물 텍스트에서 나온 결과 옆에 일반 standings 팩트로 추가되며, evidence가 텍스트가 아니라 이미지에서 왔음을 나타냅니다.
{ "index": 0, "type": "standings", "data": { "competition": "…", "round_text": "…", "rows": [ … ] }, "evidence": { "text": false, "attachments": [0] } }- 추가만 됩니다. 경기, 이벤트, 게시물 유형, 번역은 텍스트에서 나오며 이로 인해 바뀌지 않습니다. 텍스트에 이미 순위표가 있으면 추가되지 않습니다.
- 값은 인쇄된 그대로 복사합니다. 깨끗한 숫자가 아닌 칸(읽을 수 없거나 글자가 섞인 경우)은
null이며, 계산하거나 고치지 않습니다. 행이 서로 맞지 않는 표(순위 번호가 건너뛰거나, 경기 수가 승+무+패와 다른 경우)는 저장되지 않습니다. - 득점·실점 열은 가져오지 않으며, 이미지의 다른 내용(경기 일정·결과 그래픽, 라인업, 사진)은 읽지 않습니다. 순위표는 텍스트가 구조화 결과를 만들었는지와 관계없이 읽습니다. 구조화 결과가 있으면 팩트는
structured.facts에 들어가고, 없으면(캡션 없이 이미지만 있는 게시물, 또는 텍스트 처리에 실패한 게시물)structured: null옆의attachmentFacts로 제공됩니다. 텍스트 결과와 합쳐지지 않으며,processing은 여전히 텍스트 처리만 나타냅니다. - 다른 파생 데이터와 마찬가지로 누락되거나 틀릴 수 있습니다. 이미지 자체는 이 API가 제공하지 않습니다.
대표팀 소집 게시물과 그 팩트(이 페이지 예제의 첫 항목):
curl "https://api.chizumulu.net/v1/structured?limit=2&cursor=MjAyNi0wOS0xOVQxNToyMDo1Mi4wMDBafDQ="대표팀 소집 게시물: call_up과 community 팩트, 한국어 번역 포함
{ "items": [ { "rawUpdateId": 3, "source": "whatsapp", "sourceMessageId": "3EB0A1F2C4D5E6F70003", "sourceServerId": null, "sourceTimestamp": "2026-09-19T14:10:05.000Z", "observationCount": 1, "latestObservation": { "id": 3, "observationId": "8f1c6a52-3b7e-4c1a-9d20-0a1b2c3d4e03", "receivedAt": "2026-09-19T14:10:07.330Z", "text": "Congratulations to Chizumulu United's Mayamiko Chiusiwa on his call-up to the Flames U23 squad!\n\n#CINRFALeagueOne #NRFATransformingTheGame" }, "processing": { "state": "done", "method": "ai", "model": "deepseek/deepseek-v4-flash-0731", "postType": "community", "translationState": "done", "reusedFromObservationId": null, "error": null }, "structured": { "schemaVersion": 2, "postType": "community", "competition": { "code": "nrfa_league_one", "conflict": false, "evidence": [ { "code": "nrfa_league_one", "signal": "#CINRFALeagueOne", "kind": "hashtag" } ] }, "matches": [], "facts": [ { "index": 0, "type": "call_up", "data": { "player": "Mayamiko Chiusiwa", "club": "Chizumulu United", "position": null, "called_to": "Flames U23", "squad_text": null, "competition": null, "opponent": null, "first_leg_date_text": null, "camp_date_text": null }, "evidence": { "text": true, "attachments": [] } }, { "index": 1, "type": "community", "data": { "occasion": "congratulations", "recipients": [ "Mayamiko Chiusiwa" ] }, "evidence": { "text": true, "attachments": [] } } ] }, "korean": { "text": "치주물루 유나이티드의 마야미코 치우시와가 Flames U23 대표팀에 소집된 것을 축하합니다!\n\n#CINRFALeagueOne #NRFATransformingTheGame", "model": "deepseek/deepseek-v4-flash-0731" } }, { "rawUpdateId": 2, "source": "whatsapp", "sourceMessageId": "3EB0A1F2C4D5E6F70002", "sourceServerId": null, "sourceTimestamp": "2026-09-19T04:48:18.000Z", "observationCount": 1, "latestObservation": { "id": 2, "observationId": "8f1c6a52-3b7e-4c1a-9d20-0a1b2c3d4e02", "receivedAt": "2026-09-19T04:48:20.951Z", "text": "Matchday live" }, "processing": { "state": "awaiting_ai", "method": null, "model": null, "postType": null, "translationState": "none", "reusedFromObservationId": null, "error": null }, "structured": null, "korean": null } ], "nextCursor": "MjAyNi0wOS0xOVQwNDo0ODoxOC4wMDBafDI="}대회 텍스트 vs 대회 코드
섹션 제목: “대회 텍스트 vs 대회 코드”의도적으로 나뉜 두 가지입니다.
competition(경기의 필드)은 텍스트입니다. 대회 이름이 게시물에 단어로 적혀 있을 때만 적힌 그대로 복사합니다(NRFA LEAGUE 2,Chiwemi Investment NRFA League One. 단어가 있다면#뒤여도 됩니다:#NRFA LEAGUE 2는NRFA LEAGUE 2가 됩니다).#CINRFALeagueOne이나#NRFADivisionLeague2같은 인코딩된 해시태그는 대회 이름이 아닙니다. 복사하지도 풀어 쓰지도 않습니다. 그런 해시태그만 있으면competition은null입니다.competitionCode는 분류입니다. 게시물 속 알려진 표현과 해시태그로 고정된 규칙에 따라 결정됩니다. 현재 값은nrfa_league_one,nrfa_league_two이며, 모호하지 않은 신호가 없으면null입니다.competitionBasis는 경기가 코드를 얻은 방식입니다.match-text(그 경기 자신의competition텍스트에서),post-signal(해시태그처럼 게시물의 다른 곳에 있는 모호하지 않은 신호 하나에서 상속), 또는null.structured.competition은 게시물 수준의 시각입니다.code,conflict(게시물에 둘 이상의 대회 신호가 있으면true이며 이때code는null),evidence(코드를 만든 신호들. 각각{ code, signal, kind }이고kind는hashtag또는text).
대회로 거를 때는 competitionCode를 쓰세요. 게시물이 쓴 말 그대로가 필요할 때만 competition을 쓰세요.