뜯어본 것공개

public-apis

별 47만 개짜리 목록의 링크 검사가 400일 연속 실패하고 있었습니다. 그리고 제가 찾던 목록은 이미 제 폴더 안에 있었습니다.

1,756건 · 게이트 400일github.com/public-apis/public-apis ↗

목록은 진짜인데 「살아 있는 목록」은 아니었습니다. 별 47만 개가 붙은 이 저장소는 누구나 쓸 수 있는 공개 API를 표 한 장에 모아 둔 것입니다. 그 표에 매일 도는 링크 검사가 붙어 있는데, 400일 연속 실패하고 있었습니다. 원인이 링크가 많아서가 아니라 중복 URL 네 건이 검사 앞단에서 스크립트를 죽여 생사 검사가 한 번도 실행되지 않은 것이었습니다. 그 상태로 무작위 300건을 직접 불러 보니 아홉 중 하나가 확실히 죽어 있었습니다. 그런데 이 편에서 제일 값비싼 사실은 대상이 아니라 제 쪽에 있었습니다 — 제가 찾으러 간 목록의 83.5%는 이미 제 폴더 안 카탈로그에 들어 있었고, 그 카탈로그에만 있는 한국 공공 API 619건은 원본에 아예 없습니다.

저장소의 실체 — 표 한 장과 그것을 지키는 스크립트 둘 ① 기여자 — 표에 한 줄 인증 · HTTPS · CORS 3열 PR 하나에 링크 하나 ② 게이트 셋이 돈다 형식 · 새 링크 · 전체 링크 사람 리뷰는 눈 하나 ③ 머지 = 배포 README.md 247KB 한 장 빌드 · 릴리스 · 버전 없음 게이트 셋 — 앞의 둘은 PR 때, 셋째만 매일 전체를 훑는다 format.py — 표 형식 열 개수 · 인증 허용값 5종 새로 추가된 링크만 PR 에서 바뀐 줄 links.py — 전체 링크 매일 00:00 UTC ⇒ 무엇이 들어오는지를 판정하는 것은 셋째 게이트 하나 기여 마찰이 0 에 가까워서 유입은 무한한데, 나머지 둘은 형식만 봅니다
저장소 안에 API 서버도 검색 UI도 패키지도 없다. 표 한 장과 그것을 지키는 게이트 셋이 전부다.

API 목록이 아니라 API 문서로 가는 링크 목록이다

이름이 주는 인상과 실체가 다릅니다. 표의 첫 칸은 호출할 엔드포인트가 아니라 문서 페이지 주소입니다. 저장소를 클론해도 나오는 것은 README.md 한 장(247KB)과 파이썬 스크립트 두 개뿐이고, 그 밖에는 아무것도 없습니다.

항목 실측 (2026-09-09)
표에서 파싱되는 항목 1,756건 · 51개 분류
저장소 크기 README 247KB + 검사기 format.py 8.5KB · links.py 8.0KB
별 / 포크 / 기여자 477,439 / 52,699 / 422명
생성 / 최종 푸시 2016-03-20 / 2026-09-05 — 방치 저장소가 아니다
머지된 PR / 대기 PR 3,695 / 1,877
라이선스 MIT — 데이터를 재가공해 내 카탈로그로 만들어도 된다
후원 README 최상단 전체가 APILayer 광고이고 저장소 홈페이지 링크도 그쪽이다

메타데이터는 세 열입니다. 그중 실제로 판단에 쓰이는 건 CORS 한 열인데, 절반 이상이 「미상」입니다.

값 분포
인증 불필요 834(47.5%) · apiKey 765(43.6%) · OAuth 150(8.5%)
HTTPS 지원 1,664(94.8%) — 사실상 전부라 필터 가치가 없다
CORS Yes 606(34.5%) · No 162(9.2%) · Unknown 988(56.3%)

여기서 값을 치르고 배운 것 하나 — 자기 신고 메타는 갱신을 강제할 방법이 없으면 시간이 지날수록 「미상」 쪽으로 무너집니다. 무너지는 방향이 「틀림」이 아니라 「미상」이라 화면에서는 아무 일도 안 일어난 것처럼 보입니다.

분류 51개 중 손에 잡히는 것은 다섯 중 하나뿐이다

키 발급 없이 브라우저에서 바로 부를 수 있어야 실용적입니다. 인증 불필요 + CORS Yes + HTTPS Yes 로 걸면 1,756 중 341건(19.4%) 이 남습니다. 분류별로는 이렇게 갈립니다.

분류 전체 바로 부를 수 있는 것
Development 160 42
Government 106 15
Games & Comics 103 34
Geocoding 96 17
Transportation 80 9
Cryptocurrency 79 12
Finance 71 10
Open Data 56 19
Social 51 4
Video 49 10
Security 48 7
Sports & Fitness 48 12
Science & Math 41 8
Health 39 14
Weather 39 9
Documents & Productivity 39 1
Machine Learning 36 8
Music 35 1
Business 32 3
Photography 31 0
Test Data 31 6
Animals 26 12
Food & Drink 26 3
Books 25 12
Personality 25 6
Art & Design · Email 24 · 24 6 · 6
Environment · Jobs · News 23 · 23 · 23 4 · 6 · 4
Currency Exchange 22 6
URL Shorteners 20 4
Anime · Text Analysis 19 · 19 8 · 0
Calendar · Cloud Storage · Shopping 18 · 18 · 18 4 · 1 · 1
Entertainment 17 6
Anti-Malware 16 1
Blockchain 15 3
Dictionaries 13 1
Tracking 11 0
Data Validation · Vehicle 10 · 10 0 · 1
Open Source Projects 9 4
Auth · Phone 7 · 7 0 · 1
CI · Programming · Patent · Events 6 · 5 · 4 · 3 전부 0

빈 칸이 정보입니다. Photography·Text Analysis·Tracking·Data Validation·인증 분류는 바로 부를 수 있는 것이 하나도 없습니다 — 그 도메인은 무료 공개가 성립하지 않는다는 뜻이고, 목록을 뒤지는 대신 처음부터 유료를 보는 편이 빠릅니다.

설계 결정 넷은 저마다 성립 조건을 하나씩 달고 있다

결정 왜 이렇게 했나 이게 깨지면 안 먹힌다
DB 대신 마크다운 표 기여 마찰을 0으로. 웹에서 연필 아이콘 눌러 한 줄 쓰면 끝 읽는 쪽이 사람일 때만. 기계로 쓰려면 매번 파싱해야 한다
PR 하나에 링크 하나 리뷰 단위를 최소화해 판단 비용을 낮춘다 유입이 처리량보다 작을 때만. 지금 대기 1,877건으로 깨져 있다
인증·HTTPS·CORS 3열 「지금 쓸 수 있나」를 목록 단계에서 거르게 한다 값이 최신일 때만. CORS 56.3%가 미상이라 절반은 죽은 필터다
링크만 검사, 응답은 안 봄 키가 없으면 실제 호출을 못 한다 문서 생존 = 서비스 생존일 때만. 도메인이 팔려 다른 사이트가 된 항목은 원리적으로 못 잡는다

네 번째의 값을 치른 결과가 실제로 최근 PR 제목에 있습니다 — “Removed repurposed site domains”(용도가 바뀐 도메인 제거). 검사기가 못 잡으니 사람이 손으로 걷어내고 있습니다.

매일 도는 링크 검사는 400일 동안 링크를 한 번도 보지 않았다

저장소에는 validate_links.yml 이 있고 cron: '0 0 * * *' 로 매일 돕니다. 기여 안내서도 “PR을 열면 전체 링크 유효성 빌드가 돈다” 고 적어 두었습니다. 그래서 실행 이력을 전량 받았습니다.

  • 최근 400회 조회 — 성공 0회. 2025-08-05 부터 2026-09-08 까지 전부 failure
  • 실패 단계는 5번 Validate all links from README.md
  • 잡 시작 00:29:49 → 실패 00:30:03. 14초입니다. 링크 1,756개를 검사한 시간이 아닙니다

로그의 마지막 다섯 줄이 원인을 그대로 보여 줍니다.

Checking for duplicate links...
Found duplicate links:
https://isitdownstatus.com
https://tastedive.com/read/api
https://api.nasa.gov
https://open-meteo.com
##[error]Process completed with exit code 1.

links.pymain() 은 중복 검사를 먼저 부르고, 그 함수는 중복을 찾으면 sys.exit(1) 로 프로세스를 끝냅니다. 바로 다음 줄에 있는 생사 검사는 영원히 도달하지 않습니다.

links = find_links_in_file(filename)
start_duplicate_links_checker(links)      # 중복이면 여기서 sys.exit(1)
if not only_duplicate_links_checker:
    start_links_working_checker(links)    # 400일 동안 도달 0회

오늘 받은 README에도 위 네 개 중 둘은 여전히 두 번씩 들어 있습니다. 값싼 검사를 비싼 검사 앞에 두고 종료로 끊으면, 비싼 검사는 한 번도 실행되지 않은 채로 「매일 돌고 있다」처럼 보입니다. 화면에 매일 빨간 X가 뜨는데도 아무도 안 고친 이유는 원인이 중복 네 건이라 사소해 보였기 때문입니다. 조용한 게이트보다 나쁜 것은 막힌 게이트입니다 — 조용한 쪽은 아무 신호도 안 주지만, 막힌 쪽은 매일 신호를 주면서 그 신호가 무의미하다는 것까지 학습시킵니다.

그래서 직접 300건을 불러 봤다 — 아홉 중 하나가 죽어 있다

1,756건에서 고정 시드로 300건을 뽑아 브라우저 UA로 GET 했습니다(타임아웃 9초, 리다이렉트 추적, 동시 30).

결과 건수 판정
정상 2xx/3xx 247 (82.3%) 살아 있음
404 · DNS 소멸 · 연결 거부 16 · 9 · 2 확실 사망 27건 = 9.0% ±3.2pp
403 14 대부분 봇 차단 — 저장소 검사기도 이건 정상으로 친다. 사망으로 세면 안 된다
타임아웃 · 인증서 만료 · 5xx · 400 11 회색지대

측정치에 신뢰구간과 라벨을 붙이는 규약은 Headroom 편에서 정한 것을 그대로 씁니다. 403을 사망으로 세면 사망률이 13.7%로 뛰는데, 그 수치는 대상을 나쁘게 보이게 만들 뿐 사실이 아닙니다.

분류별 편차가 큽니다. 암호화폐 50%(9/18) · 스포츠 50%(3/6) · 소셜 30% · 지오코딩 25% · 개발 24%가 비정상이었고, 반대로 공공데이터 0%(0/11) · 건강 0%(0/7) · 게임 6%(1/17)는 거의 멀쩡했습니다. 돈 냄새가 나는 분류일수록 잘 죽습니다. 이건 목록의 품질 문제가 아니라 그 바닥의 수명 문제입니다.

기계로 쓰려는 사람이 제일 먼저 찾는 공식 JSON API api.publicapis.orgDNS 조회 자체가 안 됩니다. 같은 시점 raw.githubusercontent.com 은 200으로 답했으니 제 회선 문제가 아닙니다. 인터넷에 남아 있는 “엔드포인트를 부르면 목록이 JSON으로 옵니다” 류의 안내는 전부 낡았습니다.

게이트는 링크가 많아서가 아니라 순서 때문에 죽었다 ① 중복 링크 검사 중복 4건 → sys.exit(1) ② 링크 생사 검사 실행 도달 0회 매일 00:00 UTC 400회 연속 실패 그 상태의 목록을 무작위 300건 직접 호출 — 브라우저 UA · 타임아웃 9초 정상 2xx/3xx 247 · 82.3% 확실 사망 27 · 9.0% ±3.2pp 회색 — 403 · 타임아웃 26 · 봇 차단이 대부분 ⇒ 목록에 있다는 것은 언젠가 살아 있었다는 뜻이다 분류 편차가 크다 — 암호화폐 50% · 스포츠 50% 가 죽었고, 공공데이터 · 건강은 0% 였다
사망률보다 중요한 것은 그것이 걸러지지 않은 채로 400일을 지났다는 사실이다.

유명한 것만 골라도 사망률은 똑같았다

목록의 메타값을 믿지 않고, 문서 페이지가 아니라 실제 데이터 엔드포인트를 열여섯 개 골라 직접 불렀습니다. 살아남은 것이 열셋(81%)입니다 — 무작위 표본의 82.3%와 사실상 같습니다. 제가 「유명하다」고 고른 것도 사망률이 다르지 않았습니다.

API 결과 쓸 자리
DiceBear pixel-art 200 · 358ms · CORS * 도트 아바타를 시드로 무한 생성 — 캐릭터 저작권을 안 건드리는 유일한 후보
Nager.Date 공휴일 200 · 418ms 영업일 계산 · 게임 안 계절 이벤트
Open-Meteo 200 · 1,881ms 데스크톱 컴패니언의 날씨 반응
Sunrise-Sunset 200 · 907ms 같은 물건의 낮/밤 상태 전환
httpbin 503/200 · ~850ms 테스트 러너의 재시도·타임아웃 회귀 — 임의 상태코드를 실물로 만들어 준다
RandomUser · DummyJSON · JSONPlaceholder 200 목 데이터
Lorem Picsum · UUID · 환율 · GitHub 200 · 276~1,545ms 보조
FakerAPI 502 죽어 있다
Random Data API DNS 소멸 죽어 있다

죽은 둘이 하필 Test Data 분류였습니다. 31건짜리 그 분류는 QA 하는 사람이 제일 먼저 여는 곳입니다. 목록에서 그럴듯한 이름 둘을 집었더니 둘 다 없는 서비스였다는 게 이 편의 실용적 결론입니다 — 쓰기 전에 한 번 부르는 30초가 목록을 믿는 것보다 싸다.

제가 찾으러 간 목록은 이미 제 폴더 안에 있었다

여기까지 쓰고 나서 제 _생성스크립트 폴더를 열었습니다. 한 달 전에 만들어 둔 것이 그대로 있었습니다 — 마크다운 표를 파싱해 검색·분류·인증·링크상태 필터가 붙은 자립형 HTML 한 장으로 굽는 생성기와, 그 산출물 1,791건짜리 카탈로그입니다. 한 자료는 딱 한 자리에 둔다는 규칙은 옵시디언 LLM 위키 편에서 가져온 것인데, 정작 그 자리를 제가 안 찾아본 것입니다.

겹침을 실측했습니다.

대조 의미
원본 1,756건 중 카탈로그가 이미 가진 것 1,466 · 83.5% 새로 도입할 이유가 없다
원본에만 있는 것 290 · 16.5% 8월 중순 이후 유입분
카탈로그에만 있는 것 619 기상 예보 · 부동산 실거래가 · 금융 공공데이터 등 한국 공공 API
카탈로그의 링크 실측 범위 한국분 300건뿐 나머지 1,491건은 미검증 — 위 사망 9%가 그대로 숨어 있다

판정이 여기서 뒤집혔습니다. 「원본이 더 크니 원본을 봐야지」가 아닙니다. 한국에서 쓰는 기준으로는 제 카탈로그가 원본보다 낫습니다 — 원본에 한국 공공데이터 포털 링크는 딱 한 줄입니다. 원본에서 가져올 것은 목록이 아니라 「8월 이후 신규 290건」이라는 델타 하나뿐이고, 카탈로그가 참조하는 한국어 파생본은 글로벌 쪽 갱신이 사실상 멈춰 있어서 그냥 당겨받는 것만으로는 그 290건이 안 들어옵니다.

판정 — 도구를 들이는 일이 아니라 이미 있는 것의 눈금을 켜는 일이었다

항목 판정 사유 · 되돌리기 비용
원본을 새 참조처로 도입 기각 이득 0 — 83.5%를 이미 갖고 있다
카탈로그에 원본 직접 파싱 소스 추가 채택 누락 290건 편입 + 이후 자동 동기 · 되돌리기 0(소스 하나 제거)
링크 실측을 전체 행으로 확대 채택 숨어 있는 사망 ≈134건[estimated] 이 화면에 걸린다 · 되돌리기 0
도트 아바타 · 날씨 · 공휴일 · httpbin 보류 실측 생존은 확인. 다만 지금 아픈 데가 없다
링크 검사기 코드 차용 기각 이 저장소에서 베낄 코드는 없다. 남길 것은 게이트 순서 함정 하나

이 편에서 값을 치른 교훈은 대상이 아니라 순서에 관한 것입니다. 게이트를 하나 더 만드는 것보다, 이미 있는 게이트가 정말 뒤까지 도달하는지를 한 번 확인하는 쪽이 값이 큽니다. 저쪽은 400일을 그렇게 보냈고, 저는 한 달 전에 만든 카탈로그의 링크 검사가 전체의 17%에만 걸려 있다는 걸 이번에 알았습니다. 둘 다 같은 모양의 결함입니다 — 돌고 있다는 신호와 일하고 있다는 사실은 다릅니다.

못 한 것

403 열네 건을 끝까지 가르지 못했습니다. 봇 차단으로 보이지만 그중 진짜로 죽은 것이 섞여 있을 수 있습니다. 가르려면 사람이 브라우저로 하나씩 열어야 하고, 그 비용이 얻는 정확도보다 큽니다. 그래서 회색으로 남겨 두고 사망률을 하한(9.0%)으로 적었습니다.

표본은 300건이라 분류별 수치는 신뢰구간이 넓습니다. 암호화폐 50%는 18건 중 9건이고, 스포츠 50%는 6건 중 3건입니다. 「분류별로 다르다」까지는 말할 수 있지만 「정확히 몇 퍼센트」는 못 말합니다.

전수 검사는 하지 않았습니다. 1,756건 전부를 부르는 것은 가능하지만, 그건 대상 저장소가 매일 하기로 해 놓고 안 하고 있는 일이고 제가 대신 해 줄 이유가 없습니다. 제 카탈로그 안의 것만 실측하면 제 쪽 목적은 끝납니다.

재개 조건은 둘입니다. ① 중복 네 건이 고쳐져 일일 검사가 실제로 통과하기 시작하면 — 그때는 저쪽 결과를 신뢰할 수 있으니 제 실측을 줄일 수 있습니다. ② 공식 JSON API가 되살아나면 파싱 대신 그쪽을 씁니다. 둘 다 한 달에 한 번 확인이면 충분합니다.