The kna CLI: Master Database
Query 115K bills, 4.1M votes, and ideal points from your terminal
Overview
The kna (Korean National Assembly) CLI provides offline, instant access to a pre-built master database that integrates 19 Open Assembly API endpoints into a single queryable interface. Unlike the live API (Chapters 3-4), kna works on processed Parquet files - no API key needed, no rate limits, no network latency.
Chapters 6-9 use kna 0.8.1, whose data were collected from the Open Assembly API mostly on 2026-09-25.
| Feature | Live API (Ch 3-4) | kna CLI |
|---|---|---|
| Bills | 16-22nd (per query) | 17-22nd (115K pre-loaded, 67 columns in every assembly) |
| Lifecycle timestamps | Requires chaining 4+ endpoints | Single row per bill, vetoes folded in |
| Roll call votes | Per-bill, rate-limited | 4.1M member-level votes (17-22nd), instant |
| Member metadata | Per-query, current assembly only | 1,948 member-terms (17-22nd), with seniority at each assembly |
| Cosponsorship | Per-bill, paginated | 1.38M bill-member rows (17-22nd), roles 대표발의 / 공동발의 / 찬성 |
| Asset disclosures | Not available | 3,215 member-year wealth rows |
| Ideal points | Not available | 955 legislator-terms (20-22nd), three series |
| Propose-reason texts | Not available | 109,756 law bills (17-22nd) |
| Speed | ~2 req/sec | Instant (local Parquet) |
Installation
Step 1: Install the CLI
pip install knaOr use pipx install kna which handles PATH automatically. This chapter needs version 0.8.1 or later, which kna --version reports.
Step 2: Get the data
The PyPI package contains the code only. The CLI needs processed Parquet data files hosted via Git LFS:
# Install Git LFS (one-time)
brew install git-lfs # macOS
# or: sudo apt install git-lfs # Ubuntu/Debian
git lfs install
# Clone with data
git clone https://github.com/kyusik-yang/kna.git
cd knaIf you already cloned without LFS, parquet files will be tiny pointer files and kna info will fail. Fix with: git lfs install && git lfs pull
Step 3: Point the CLI to the data
export KBL_DATA=~/kna/data/processed
# Make it permanent
echo 'export KBL_DATA="$HOME/kna/data/processed"' >> ~/.zshrc
source ~/.zshrc
# Verify
kna infoQuick start
Database overview
kna info Korean National Assembly Database
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┓
┃ Assembly ┃ Bills ┃ Laws ┃ Enacted ┃ Promulg. ┃ Cols ┃
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━┩
│ 17th (2004-08) │ 8,368 │ 7,489 │ 1,913 │ 1,913 │ 67 │
│ 18th (2008-12) │ 14,762 │ 13,913 │ 2,353 │ 2,353 │ 67 │
│ 19th (2012-16) │ 18,735 │ 17,822 │ 2,793 │ 2,793 │ 67 │
│ 20th (2016-20) │ 24,996 │ 24,141 │ 3,195 │ 3,195 │ 67 │
│ 21st (2020-24) │ 26,707 │ 25,858 │ 2,959 │ 2,959 │ 67 │
│ 22nd (2024-) │ 21,581 │ 21,022 │ 1,644 │ 1,555 │ 67 │
├────────────────────┼──────────┼──────────┼──────────┼──────────┼───────┤
│ Total │ 115,149 │ 110,245 │ 14,857 │ 14,768 │ │
└────────────────────┴──────────┴──────────┴──────────┴──────────┴───────┘
Laws = 법률안. Enacted = 법률안 원안가결 + 수정가결 (final result).
Promulg. = 법률안 with a promulgation date (became law).
Roll call votes 4,147,402 (17-22nd, member-level votes)
16th votes 923 (experimental, fragmentary, separate file)
Ideal points 955 (20-22nd, bridged W-NOMINATE, v20260917)
Members 1,948 (17-22nd, party/district/committee)
Asset disclosures 3,215 (19-22nd, member-year wealth)
Committee mtgs 818,448 (17-22nd)
Bill texts 109,829 (17-22nd law bills, propose-reason)
Cosponsor edges 1,379,763 (17-22nd, 대표/공동발의 + 찬성)
Subcommittee 93,981 (17-22nd, 소위 referrals/reviews)
Alt. absorption 25,439 (17-22nd, 대안 -> absorbed bills)
Committee spells 13,616 (dated committee assignments)
Vote events 16,465 (17-22nd, tallies and minutes votes)
Veto events 47 (17-22nd, 재의요구)
Data freshness 2026-09-23
The 17th-19th roll calls are the name lists printed in the plenary minutes. The 20th-22nd come from the API, and the 22nd votes of 16 members that the vote API omits come from LIKMS. The 16th vote rows are parsed from speech text. They are not a roll-call matrix, so kna keeps them in a separate experimental file.
Search bills by title
# Keyword search
kna search "인공지능" --assembly 22
# Combine filters
kna search "부동산" --assembly 22 --status enacted --committee 국토
# By proposer
kna search "조세특례제한법" --proposer 박범계 --assembly 21 -n 30Filter options:
| Option | Description | Values |
|---|---|---|
--assembly |
Assembly number | 17-22 |
--committee |
Committee name (partial match) | e.g. 과방, 국토 |
--proposer |
Lead proposer name (exact match, joint leads included) | e.g. 김영식 |
--status |
Status group | passed, enacted, reflected, pending, rejected, expired |
--kind |
Bill type | 법률안, 결의안, 동의안 |
--from / --to |
Proposal date range | YYYY-MM-DD |
-n |
Max results (default 20) | integer |
The status groups are passed (원안가결, 수정가결, 대안반영폐기), enacted (원안가결, 수정가결), reflected (bills with law_reflected = 1), pending (계류중), rejected (부결, 폐기, 철회) and expired (임기만료폐기). A vetoed bill counts as enacted only if it was passed again.
Search propose-reason texts
search queries bill titles. text searches within the full propose-reason text (제안이유) - the substantive rationale behind each bill.
# Find bills mentioning climate change in their rationale
kna text "기후변화" --assembly 22
# Find bills discussing AI regulation rationale
kna text "인공지능 규제" -n 10This covers the 110,245 law bills of the 17th-22nd Assembly, and 109,756 of them, or 99.6%, have a propose-reason text.
Bill lifecycle timeline
The “killer feature” of kna - see any bill’s full journey through the legislative process:
kna show 2213549약사법 일부개정법률안
bill_no 2213549
assembly 22nd
kind 법률안
proposer 남인순의원 등 13인
committee 보건복지위원회
status 수정가결
LIFECYCLE date days
● 발의 2025-10-13 -
● 소관위 회부 2025-10-14 +1
● 소관위 상정 2025-11-12 +30
● 소관위 처리 2026-04-29 +198
● 법사위 회부 2026-04-29 +198
● 본회의 의결 2026-08-26 +317
● 공포 2026-09-22 +344
VOTE 찬성 205 / 반대 1 / 기권 6 (재적 299)
PROPOSE REASON
현행법령은 의약품도매상 또는 의약품의 품목허가를 받은 자의 영업소로 오인하게
할 우려가 있는 표시, ... (486 chars total)
http://likms.assembly.go.kr/bill/billDetail.do?billId=PRC_B2C5A1A0Z1A0Y1Z0G2H2F2G6E9C4D1&ageFrom=22&ageTo=22
Each stage shows:
- ● reached (with date and days since proposal)
- ○ not reached (bill did not advance to this stage)
Lifecycle stages: 발의 → 소관위 회부 → 소관위 상정 → 소관위 처리 → 법사위 회부 → 본회의 의결 → 공포
A bill without a floor decision shows its final disposition in place of 본회의 의결, for example 처리 (대안반영폐기) for a bill absorbed into a committee alternative. A vetoed bill adds the presidential reconsideration request and the re-vote, and its tally is that of the first passage:
kna show 2200851 status 수정가결
vetoed 재의요구 after 수정가결
...
● 본회의 의결 2024-12-26 +185
● 재의요구 2025-01-22 +212
● 재의결 (수정가결) 2025-04-17 +297
● 공포 2025-04-22 +302
VOTE 찬성 161 / 반대 94 / 기권 6 (재적 300), first passage
Legislator profiles
kna legislator 이재명 --assembly 22 이재명 (IUD9392R) · 22nd Assembly
────────────────────────────────────────
party 더불어민주당 (at election)
district 인천 계양구을
committee 외교통일위원회
election 지역구 (재선 in the 22nd)
ideal point -0.359 (22nd, bridged W-NOMINATE, v20260917)
rank 173 / 320 in the 22nd (← 진보 ··· 보수 →)
BILL RECORD (lead proposer, including joint leads)
led 4 bills
enacted 0 (0.0%)
promulgated 0
alt-reflected 2
median days 316d (proposal → processing)
Shows:
- Party at election, district and the committees served in that assembly
- Seniority at that assembly (초선, 재선, …), not the lifetime count
- Bridged W-NOMINATE ideal point (ideological position) with its vintage
- Rank within the assembly (← liberal … conservative →)
- Bill record: bills led, enacted count and rate, promulgated count
- Top enacted bills with dates
Names are matched exactly, and some legislators share a name. The 22nd Assembly has two members named 박지원, so the CLI lists both MONA_CDs and asks for one:
kna legislator 박지원 --assembly 22
# 2 legislators named 박지원 in the 22nd Assembly:
# 8BF5855P 22nd 더불어민주당 전남 해남군완도군진도군
# H7X3372O 22nd 더불어민주당 전북 군산시김제시부안군을
# Pick one by MONA_CD (the name is still required)
kna legislator 박지원 --assembly 22 --mona 8BF5855P
# Without --assembly, one row per term served
kna legislator 추미애Ideal points are available for the 20th-22nd Assembly only (955 legislator-terms). The 17th-19th roll calls from the plenary minutes are not scaled, so for the 17th-19th the bill record is shown without ideological positioning. The 22nd is in session, and its values are provisional. 한동훈, seated in June 2026, has too few contested votes for a 22nd ideal point.
Aggregate statistics
Legislative funnel
How many bills survive each stage of the legislative process?
kna stats funnel --assembly 22 22nd Assembly · Legislative Funnel (법률안 only)
Stage Bills Rate
발의 21,022 100.0% ██████████████████████████████
소관위 회부 20,966 99.7% █████████████████████████████
소관위 상정 17,687 84.1% █████████████████████████
소관위 처리 6,396 30.4% █████████
법사위 회부 1,729 8.2% ██
본회의 의결 1,673 8.0% ██
공포 1,555 7.4% ██
Each stage counts bills that reached it or a later stage.
본회의 의결 = floor decision (원안가결, 수정가결, 부결); 공포 = promulgated.
Counting each stage as “reached it or a later stage” keeps the funnel from widening when a bill skips a stage. Committee alternatives (위원장 대안) are never referred to a committee, and bills of the 법제사법위원회 itself skip the 법사위 review.
Passage rate trend
kna stats passage-rate Passage Rate Trend · 17-22nd Assembly (법률안 only)
Assembly Total Passed Rate Enacted Rate Promul. Rate
17th 7,489 3,812 50.9% 1,913 25.5% 1,913 25.5%
18th 13,913 6,178 44.4% 2,353 16.9% 2,353 16.9%
19th 17,822 7,456 41.8% 2,793 15.7% 2,793 15.7%
20th 24,141 8,758 36.3% 3,195 13.2% 3,195 13.2%
21st 25,858 8,906 34.4% 2,959 11.4% 2,959 11.4%
22nd 21,022 5,850 27.8% 1,644 7.8% 1,555 7.4%
passed = 원안가결 + 수정가결 + 대안반영폐기
enacted = 원안가결 + 수정가결 (final result: a vetoed bill counts only if re-passed)
promulgated = promulgation date recorded (became law)
The enacted rate declines from 25.5% (17th) to 11.4% (21st) - legislative inflation at work. The 22nd is still in session, so its 7.8% is not final.
Data export
Extract filtered subsets for downstream analysis in R, Stata, or Python:
# Health committee bills, enacted only
kna export health.csv --assembly 22 --committee 보건복지 --status enacted
# All 22nd Assembly bills as Parquet
kna export all_22.parquet --assembly 22
# All law bills (법률안) across all assemblies
kna export laws.csv --kind 법률안Format auto-detected from extension: .csv, .parquet, .tsv. kna export writes bills only, with all 67 master columns. To keep, say, government bills only, filter the export on ppsr_kind == "정부".
Using kna data in Python
The kna package also works as a Python library:
from kna.data import BillDB
db = BillDB() # reads KBL_DATA, or data/processed in a kna checkout
# Load all 22nd Assembly bills
bills = db.bills(assembly=22)
print(f"{len(bills):,} bills, {len(bills.columns)} columns") # 21,581 bills, 67 columns
# Load with column pruning (fast)
df = db.bills(assembly=22, columns=["bill_id", "bill_nm", "status", "ppsl_dt",
"enacted", "promulgated", "vetoed"])
# Ideal points: bridged W-NOMINATE by default, in the column ideal_point
ip = db.ideal_points() # 955 legislator-terms, positive = conservative
dw = db.ideal_points(series="dwnominate") # or series="wnominate"
# Roll call votes (17th-22nd): party = party at election, party_api = API label
votes = db.roll_calls(assembly=22) # 549,513 member-level votes
tallies = db.vote_events(assembly=22) # 1,847 plenary tallies
vetoes = db.veto_events() # 47 presidential vetoes (재의요구)
# Bill texts
texts = db.bill_texts() # 109,829 rows, 17th-22nd law bills
# Member metadata (party at election, district, seniority at this assembly,
# committees served in this assembly, sex, birth date)
members = db.members(assembly=22) # 321 members
spells = db.committee_assignments(assembly=22) # 1,763 dated committee spells
# Cosponsorship: one row per bill and member, role 대표발의, 공동발의 or 찬성
edges = db.cosponsorship_edges(assembly=22) # 255,415 bill-member rows
# Asset disclosures (net_worth, real estate, stocks, etc. in 천원)
assets = db.assets(assembly=22) # 586 member-year wealth rows
# Committee meetings (lowercase columns in every assembly)
meetings = db.committee_meetings(22) # 137,152 records- There is no
kna.load(). Load every table throughBillDB, or read the files indata/processed/directly. - Join legislators on MONA_CD, never on the name. It is
mona_cdin the member tables andmember_idin the roll calls, ideal points and cosponsorship edges. Same-name pairs exist in most assemblies, for example 박지원 in the 22nd. reelectionis the lifetime count at the time of collection. For seniority at a given assembly (초선, 재선, …), useterm_numberorseniority.partyis the party at election and keeps the satellite list parties, such as 더불어민주연합 and 국민의미래 in the 22nd.party_currentgives the current party of serving 22nd members.ideal_pointhas positive values for conservatives. Do not flip its sign.
Using kna data in R
library(arrow)
library(dplyr)
library(readr)
# Paths are relative to the root of the kna checkout
# Load master bills
master <- read_parquet("data/processed/master_bills_22.parquet")
# All assemblies (the same 67 columns in every file)
all_bills <- bind_rows(
lapply(17:22, function(age)
read_parquet(sprintf("data/processed/master_bills_%d.parquet", age)))
)
# Ideal points: bridged series, positive = conservative (no sign flip)
ip <- read_csv("data/processed/ideal_points_bridged.csv",
col_types = cols(member_id = col_character())) %>%
rename(ideal_point = bridged_1d)
# Members, with seniority at this assembly (term_number 1 = 초선)
members <- read_parquet("data/processed/members_22.parquet") %>%
mutate(first_term = term_number == 1)
# Bill texts
texts <- read_parquet("data/processed/bill_texts_linked.parquet")Command reference
| Command | Description |
|---|---|
kna info |
Database overview |
kna search KEYWORD |
Search bill titles |
kna text KEYWORD |
Search propose-reason texts |
kna show BILL_NO |
Bill detail + lifecycle timeline |
kna legislator NAME |
Legislator profile + ideal point (--mona for shared names) |
kna stats funnel |
Legislative funnel |
kna stats passage-rate |
Cross-assembly passage rates |
kna export PATH |
Export filtered bills to CSV/Parquet |
Companion data
For hearing and audit transcripts (15.1M speaker turns), see the companion dataset:
- kr-hearings-data - Korean National Assembly hearing transcripts (2000-2026)