public-apis
별 47만 개짜리 목록의 링크 검사가 400일 연속 실패하고 있었습니다. 그리고 제가 찾던 목록은 이미 제 폴더 안에 있었습니다.
목록은 진짜인데 「살아 있는 목록」은 아니었습니다. 별 47만 개가 붙은 이 저장소는 누구나 쓸 수 있는 공개 API를 표 한 장에 모아 둔 것입니다. 그 표에 매일 도는 링크 검사가 붙어 있는데, 400일 연속 실패하고 있었습니다. 원인이 링크가 많아서가 아니라 중복 URL 네 건이 검사 앞단에서 스크립트를 죽여 생사 검사가 한 번도 실행되지 않은 것이었습니다. 그 상태로 무작위 300건을 직접 불러 보니 아홉 중 하나가 확실히 죽어 있었습니다. 그런데 이 편에서 제일 값비싼 사실은 대상이 아니라 제 쪽에 있었습니다 — 제가 찾으러 간 목록의 83.5%는 이미 제 폴더 안 카탈로그에 들어 있었고, 그 카탈로그에만 있는 한국 공공 API 619건은 원본에 아예 없습니다.
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.py 의 main() 은 중복 검사를 먼저 부르고, 그 함수는 중복을 찾으면 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.org 는 DNS 조회 자체가 안 됩니다. 같은 시점 raw.githubusercontent.com 은 200으로 답했으니 제 회선 문제가 아닙니다. 인터넷에 남아 있는 “엔드포인트를 부르면 목록이 JSON으로 옵니다” 류의 안내는 전부 낡았습니다.
유명한 것만 골라도 사망률은 똑같았다
목록의 메타값을 믿지 않고, 문서 페이지가 아니라 실제 데이터 엔드포인트를 열여섯 개 골라 직접 불렀습니다. 살아남은 것이 열셋(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가 되살아나면 파싱 대신 그쪽을 씁니다. 둘 다 한 달에 한 번 확인이면 충분합니다.