Skip to content

Add climb13ers as a first-class source in peak_db

For: the peak_db project · From: mtn_research · Date: 2026-08-20

peak_db already treats three sources as first-class, each with an id and a URL column:

source id column url column
Lists of John (the peak's own id) loj_url
14ers.com fourteeners_id fourteeners_url
Peakbagger peakbagger_id peakbagger_url

climb13ers.com is the fourth source mtn_research reads, and it has no column. Every report's YDS class and route character comes from a climb13ers page, and the URL for that page is currently derived at build time — which is a defect, described below. This asks for climb13ers_slug / climb13ers_url alongside the others.


Why derivation cannot work

climb13ers builds its peak URLs from the pre-LiDAR elevation:

https://www.climb13ers.com/colorado-13ers/un13158   →  titled "UN 13155 [Formerly UN 13158]"

The slug is un13158; the peak is today's 13,155 ft summit. mtn_research derived the URL from the current elevation instead, producing un13155 — which does not 404. It returns a real page for whatever unrelated peak still owns that old number:

derived URL what it actually is distance from the intended peak
un13155 "UN 13166", San Luis Pass / Creede, LoJ 648, Class 3 ~25 mi
un13110 "UN 13121", Weminuche / Silverton, LoJ 703 ~30 mi

Both were cached and cited as the source of a safety-critical class rating for a report about peaks in the Uncompahgre Wilderness. Nothing detected it — the existing provenance check confirms a cited URL is cached, never that it is the right peak. A human reading the page caught it.

The same collision runs the other way, which is what makes it dangerous rather than merely wrong: peak_db 280's pre_lidar_elevation_ft is 13,541 — the same number as peak_db 316's current elevation_ft, two unrelated peaks ~90 miles apart, one in the Sangre de Cristo and one in the Sawatch.

Why peak_db is the right home

The mapping was solved using a field peak_db already has: pre_lidar_elevation_ft. That is precisely the number climb13ers keys its slugs to, so the link is a lookup, not an inference. Everything else tried first — coordinates, names, disambiguator letters, map markers — was working around a column that was already there.

Storing the resulting slug in peak_db means:

  • the fact lives with the other three source links, not in a downstream JSON file
  • every consumer gets it, not just mtn_research
  • it is reviewable in one place when climb13ers renames a peak
  • mtn_research stops deriving URLs entirely — the defect above becomes impossible

The data

A complete mapping for all 597 Colorado 13ers is attached as climb13ers_map.json, produced by mtn_research/scripts/sweep_climb13ers.py. Every peak is linked; none are unmapped. Each row records how it was established, and that provenance should be preserved on import rather than flattened:

how count meaning
PRELIDAR 270 slug built from pre_lidar_elevation_ft, and the page's current elevation matches elevation_ft. Two keys, no inference.
VERIFIED 302 a labelled summit marker on the page's own CalTopo map sits within 1500 ft of the peak and its title elevation matches on all three sides (marker, page, peak_db). Author-sourced position.
EXACT 17 the page cross-references listsofjohn.com/peak/<id> — a published fact, position unconfirmed.
REVIEWED 1 Kyle confirmed it by eye. See climb13ers_reviewed.json, which records what he was shown.
MATCH 7 older page template with no LoJ link; joined on current elevation + name. A judgement — the only soft tier.

Format:

{
  "peaks": {
    "645": { "slug": "un13158", "how": "PRELIDAR", "caltopo": "K5DR" }
  }
}

slug appends to https://www.climb13ers.com/colorado-13ers/.

What is being asked for

  1. Two columns, matching the existing convention:
  2. climb13ers_slug — e.g. un13158
  3. climb13ers_url — e.g. https://www.climb13ers.com/colorado-13ers/un13158
  4. Import the 597 rows from climb13ers_map.json.
  5. Keep the provenance. A climb13ers_link_how column, or a note, so the 7 MATCH rows stay visibly weaker than the other 590. Flattening them to "linked" loses the one thing that says which rows are safe to trust blindly. If a provenance column is unwelcome, the fallback is importing the 590 non-MATCH rows and leaving those 7.
  6. Nothing else. climb13ers disagrees with 14ers/peak_db about elevations, names and disambiguator letters — peak_db 248 is Pt 13,580A while climb13ers calls the same summit "UN 13543 Formerly UN 13580 B". 14ers remains the truth for the facts; climb13ers contributes only the link.

The 7 rows resting on a name judgement

Everything else is established by a key. These seven are joined on elevation + name, and were reviewed here before handing over — all seven are correct, and the reason each could not be confirmed mechanically is given:

peak_db id peak_db name ft climb13ers slug why not confirmed
255 Rosalie Pk 13,579 rosalie-peak page links no CalTopo map
292 Treasure Mtn A 13,535 treasure-mountain page links no CalTopo map
333 Three, Pk 13,487 peak-three page links no CalTopo map
335 Two, Pk 13,475 peak-two page links no CalTopo map
310 Bridal Pk 13,514 t-11--bridal-peak marker T.11 sits 76 ft away but carries no elevation
468 Sunshine Mtn A 13,329 sunshine-mountain marker Sunshine Mtn. sits 121 ft away, no elevation
451 Brown Mtn A 13,347 brown-mountain its map has trailhead markers only, nothing on the summit

For 310 and 468 the marker is right on the peak and clearly correct; it simply carries no second key. Confirming on name alone was tried and measurably made the whole table worse (MATCH 7 → 11, VERIFIED 302 → 285), because name-only markers also match peaks the elevation key had already settled, producing two candidate ids for one page. Left as judgements deliberately.

One row was resolved by Kyle rather than by a rule and is recorded separately, in climb13ers_reviewed.json:

  • 358 Kendall Pkkendall-peak. The only peak in 597 where the two sources disagree on the ELEVATION itself: peak_db 13,455 ft (pre-LiDAR 13,451), climb13ers' summit marker titled 13,445 - Lidar and 363 ft away. Both keys missed by single-digit feet. Kyle was shown the page, the map and peak_db's three source links and confirmed the same peak. 14ers/peak_db stays the truth for the elevation.

(san-juaquin-ridge is climb13ers' own misspelling in its slug; their page text spells it correctly, which is why it matched at all — it is now VERIFIED by its map marker.)

Two facts worth writing down somewhere

Both are load-bearing and neither is stated anywhere I could find:

  1. A peak's id IS its Lists of John id. loj_url for peak 316 is listsofjohn.com/peak/316. Consumers rely on this — it is how the climb13ers pages that carry a LoJ cross-reference were resolved at all — and it would be an unpleasant surprise if the two ever diverged.
  2. Out-of-state peaks carry NEGATIVE ids. Mount Barnard and Mount Keith (CA), Gannett (WY), Kings (UT), Mauna Kea (HI) all sit in the 13,000–14,000 ft band. Filtering a "Colorado 13er" census on elevation alone silently includes 15 of them. state is the correct filter and is populated; the negative-id convention is not obvious and cost time here.

Data-quality observations (low priority, no action needed)

Encountered while matching names; noting them since they affect every consumer that joins on display_name:

  • Inconsistent case: Pt 13,580A vs PT 13,543 — same format, different capitalisation.
  • Inverted forms: Three, Pk, Two, Pk, Eleven, Pk, Nine, Pk sort nicely but do not match the natural "Peak Three".
  • Abbreviations vary: Mtn, Pk, BM against other rows spelling them out.
  • Disambiguator letters disagree with climb13ers. peak_db Pt 13,580A is climb13ers "UN 13543 Formerly UN 13580 B"; peak_db PT 13,540 B is their "13540 A". Not a peak_db error — the two sources simply lettered a split pair in opposite order — but it means letters must never be used as a join key, in either direction.

None of these blocked anything; pre_lidar_elevation_ft made the names unnecessary.

Optional, worth considering

  • caltopo map id. 619 of 650 climb13ers pages link a CalTopo map with labelled summit markers. It is already in the attached data and is what a human opens to settle an ambiguity by eye.
  • A staleness check. The site's all-peaks listing at /colorado-13ers/peaks is a single fetch containing every slug, current elevation and "Formerly UN" alias. Hashing it detects any rename; only then does anything need re-fetching. There is no need to re-crawl 650 pages to stay current.

Once this lands

mtn_research will read climb13ers_url from peak_db and delete both its local mapping table and the URL-derivation code. The gate that currently catches wrong-peak citations (check_climb13ers_url.py) stays, but binds against peak_db instead of a local file.

Tracking issue: mtn_research#22.