Search mountainproject.com for climbing routes with the full route-finder filter surface (area, grade range across YDS/V/WI/AI/M/Aid, route type, pitches, stars, sort, pagination), returning structured per-route data including id, grade, type, pitches, length, star rating, vote count, area breadcrumb, lat/lng, and first ascent.
Works with
AI-first code editor with Composer
Before installing skills in Cursor, ensure your development environment meets these requirements:
node --versionsearch-routesExecute the skills CLI command in your project's root directory to begin installation:
Fetches search-routes from mountainproject.com/search-routes-romkbp and configures it for Cursor.
The CLI shows a list of agents. Use arrow keys and space to select Cursor:
Confirm successful installation by checking the skill directory location:
Restart Cursor to activate search-routes. Access via /search-routes in your agent's command palette.
We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.
Skills execute code in your environment. Always review source, verify the publisher, and test in isolation before production.
Submit your Claude Code skill and start earning
Automate repetitive workflows and reduce manual effort
Example
Generate reports, summarize documents, draft communications
Save 3-5 hours per week on routine tasks
Learn new skills, understand complex topics, get expert guidance
Example
Explain concepts, provide examples, suggest learning resources
Accelerate learning and skill development by 2x
Enhance output quality through reviews, suggestions, and refinements
Example
Review drafts, suggest improvements, catch errors
Improve work quality by 30-40% with less effort
0
total installs
0
this week
0
upvotes
Run in your terminal
0
installs
0
this week
—
stars
| name | search-routes |
| title | Mountain Project Route Search |
| description | >- Search mountainproject.com for climbing routes with the full route-finder filter surface (area, grade range across YDS/V/WI/AI/M/Aid, route type, pitches, stars, sort, pagination), returning structured per-route data including id, grade, type, pitches, length, star rating, vote count, area breadcrumb, lat/lng, and first ascent. |
| website | mountainproject.com |
| category | outdoor-recreation |
| tags | - climbing - rock-climbing - bouldering - route-database - search - csv-export - url-param |
| source | 'browserbase: agent-runtime 2026-05-18' |
| updated | '2026-05-18' |
| recommended_method | url-param |
| alternative_methods | - method: url-param rationale: >- Primary path. /route-finder and /route-finder-export are plain GET endpoints — the entire filter set is URL-encoded and bookmark-stable. /route-finder-export returns CSV with most of the desired fields in one request (capped at 1000 rows). /ajax/public/search/suggestions?q= resolves area names to integer IDs cookieless. - method: hybrid rationale: >- For fields the CSV doesn't include (vote_count, first_ascent, precise lat/lng to 8 decimals), fall through to /route-finder HTML rows (vote_count, area breadcrumb) and /route/<id>/<slug> detail pages (JSON-LD aggregateRating + GeoCoordinates, plus the description-details table for FA + multi-pitch length/pitches). All HTTP GET, no JS execution required. - method: api rationale: >- AVOID. The documented /data/get-routes and /data/get-routes-for-lat-lon JSON endpoints are apiKey-gated and return 403 {"success":0,"message":"Invalid Key"} for cookieless requests. The key is only obtainable from a logged-in user's /data/ profile — not usable from a stateless agent. The CSV export covers ~90% of the same field surface without auth. - method: browser rationale: >- Last-resort fallback only. The route-finder is server-rendered with all data inline in the HTML, so a browser session adds no information over cookieless fetch. The one case a browser helps is if Mountain Project tightens anti-bot in the future and a residential-proxy fetch starts returning 403 — a verified stealth browser session would still get through. |
| verified | true |
| proxies | true |
Search mountainproject.com — the public crowd-sourced US/worldwide rock-climbing route database — for routes matching a structured filter set OR a free-form text query, and return per-route structured data: id, name, URL, grade (in YDS / V / WI / AI / M / Aid), route type(s), pitches, length, star rating, vote count, area-breadcrumb path, lat/lng, and first ascent. Read-only — never logs in, never edits or rates routes.
Mountain Project's /route-finder is a plain HTTP GET form (no CSRF on read, no auth required for cookieless fetches via residential proxy) and ships a sister /route-finder-export endpoint that returns the same query result as a CSV. Lead with the CSV; only fall back to HTML scraping when you need fields the CSV doesn't include (vote_count, first_ascent) or when results exceed the CSV's 1000-row cap. The official /data/get-routes JSON API exists but is apiKey-gated and returns 403 {"success":0,"message":"Invalid Key"} cookieless — don't try it.
Mountain Project's route-finder filters by integer area ID, not by free-text. Resolve a name like "Boulder Canyon" via the public autocomplete:
GET https://www.mountainproject.com/ajax/public/search/suggestions?q=<URL-encoded name>
Response:
{
"totalResults": 10000,
"html": "<div class=\"suggestion-results\">
<div class=\"section\">Areas</div>
<a class=\"suggestion\" href=\"https://www.mountainproject.com/area/105744222/boulder-canyon?...\">
Boulder Canyon <div class=\"right-text\">Colorado</div>
</a>
<a class=\"suggestion\" href=\"https://www.mountainproject.com/area/118874741/boulder-canyon?...\">
Boulder Canyon <div class=\"right-text\">Utah</div>
</a>
..."
}
html field is an HTML string. Parse each <a class="suggestion" href="..."> — the path /area/<ID>/<slug> gives you the area ID.<div class="right-text">. Pick the match whose state matches the caller's intent. Boulder Canyon alone resolves to 3 distinct areas (Colorado=105744222, Utah=118874741, Arizona=106389009) — always disambiguate..suggestion (not .suggestion.route) in the Areas section.<a class="suggestion route"> — those include the grade inline in <div class="route-difficulty">5.7</div>. The href path is /route/<id>/<slug>.To search globally (any area), pass selectedIds=0 and skip this step entirely.
/route-finder accepts the entire filter set as GET query-string params. The URL is fully bookmark-stable — Mountain Project just URL-decodes and applies; you can hand-construct it.
GET https://www.mountainproject.com/route-finder
?selectedIds=<areaId> # 0 = global
&type=rock # rock | boulder | aid | ice | mixed
&diffMinrock=<minRank> # see internal-rank tables below
&diffMaxrock=<maxRank>
&is_trad_climb=1 # rock sub-filter — 1 includes, 0 excludes
&is_sport_climb=1 # OR-combined across the three sub-flags
&is_top_rope=0
&stars=<minStars> # 0 | 1.8 | 2.3 | 2.8 | 3.3 | 3.8
&pitches=<n> # 0=any, 1=exactly 1, 2..5=at least N, 6=6+
&sort1=<sort> # area | rating | popularity desc | title
&sort2=<sort> # tiebreaker, same options
&page=<n> # 1, 2, 3, ... (50 rows/page — NOT tunable)
Internal grade-rank tables (from option-value harvest of the route-finder form; verified 2026-05-18):
| Grade system | type= | diffMin* name | Sample values (text → rank) |
|---|---|---|---|
| YDS rock | rock | diffMinrock / diffMaxrock | 5.7→1800, 5.9→2300, 5.10a→2600, 5.10b→2700, 5.10c→3100, 5.10d→3300, 5.11a→4600, 5.11d→5300, 5.12a→6600, 5.13a→8600, 5.14a→10500, 5.15d→12400. Min and Max selects share the text labels but use slightly different internal ranks for Max (e.g. 5.10a max = 2800, 5.11d max = 5500) — the form ships both lists; cache them or extract once from /route-finder HTML. |
| Bouldering | boulder | diffMinboulder / diffMaxboulder | V0→20000, V4→20350, V10→20950, V17→21650 (min); add 50 for max (V0 max=20050, V17 max=21700) |
| Water ice | ice | diffMinice / diffMaxice | WI1→30000, WI4→32500, AI1→38000, AI6→38500 |
| Mixed | mixed | diffMinmixed / diffMaxmixed | M1→50000, M5→53500, M16→64900 |
| Aid | aid | diffMinaid / diffMaxaid | A0/C0→70000 (min), A5/C5→74500 (min); max range adds 10/750 (A5/C5 max=75260) |
These ranks are not contiguous within a grade letter — 5.10c=3100, 5.10d=3300 but 5.11a=4600 — Mountain Project pads the integer space so future intermediate grades can slot in. Always harvest the full mapping once from the form rather than guessing; see the canonical extract in this skill's screenshots #1.
The form only exposes 5 top-level type= values. "Trad", "Sport", and "Top Rope" are sub-filters of rock expressed via is_trad_climb / is_sport_climb / is_top_rope. Alpine, snow, and chossaneering are NOT filterable — they're free-form tags on individual routes but the route-finder has no checkbox for them.
For any query that returns ≤ 1000 routes, the CSV export is the fastest and most structured path:
GET https://www.mountainproject.com/route-finder-export?<same query string as /route-finder>
Returns text/csv with columns:
Route, Location, URL, "Avg Stars", "Your Stars", "Route Type", Rating, Pitches, Length, "Area Latitude", "Area Longitude"
Example row:
The Bastille Crack,"Bastille > Eldorado Canyon SP > Boulder > Colorado",https://www.mountainproject.com/route/105748490/the-bastille-crack,3.5,-1,Trad,5.7,5,350,39.93083,-105.28315
id from the URL column: /route/(\d+)/.area_path by splitting Location on > (note the spaces) and reversing — MP serializes most-specific-first; the canonical "USA → State → ... → Crag" order is the reverse of the CSV value. Some routes (international, or routes pinned at the top level) omit "USA" entirely; do not insert "USA" unless the last token is a US state name.Length is in feet (numeric, blank for unknown). Pitches is integer (blank for unknown).Avg Stars is the precise float (0.0–4.0). Your Stars is -1 cookieless (it's the requesting user's vote — useless without auth).Route Type can be a comma-separated multi-discipline string ("Trad, Sport", "Sport, TR"). Split on , then trim.Rating is the grade text (5.11d, V4, WI4). Infer grade_system from the leading characters (5. → YDS, V → V-scale, WI/AI → ice, M\d → mixed, A\d/C\d → aid).page= paging on the CSV — &page=2 returns the same first 1000 rows. For results 1001+ you must fall through to step 4.GET https://www.mountainproject.com/route-finder?<query>&page=<N>
Returns the full route-finder HTML page. 50 rows per page, hard-coded. There is no total-count number rendered — the only signal of total size is the last-page link in the pagination block:
<a href="https://www.mountainproject.com/route-finder?...&page=7">
<img src="/img/arrows/last.svg" alt="Last">
</a>
Parse page=N from the <img alt="Last"> anchor to compute total_pages. Multiply by 50 to estimate total_count (final page may be partial — fetch it to get the exact tail count). Pages beyond the last-page link return 200 OK with zero route-rows (silent — always check rows.length > 0 before claiming the page exists).
Each row is <tr class="route-row">. Extract per row:
<tr class="route-row">
<td>
<a href="https://www.mountainproject.com/route/105753505/animal-magnetism" class="text-black route-row">
<div class="float-xs-right text-xs-right">
<span class='rateYDS'>5.11c</span>
<span class='rateFrench'>7a</span> <span class='rateEwbanks'>24</span>
<span class='rateUIAA'>VIII</span> <span class='rateZA'>25</span> <span class='rateBritish'>E5 6a</span>
<div>
<span class='scoreStars'>
<img src='/img/stars/starBlue.svg'> ... <!-- integer-rounded stars -->
</span>
<span class="text-muted small"> 467</span> <!-- vote_count -->
</div>
</div>
<div class="text-truncate"><strong>Animal Magnetism</strong></div>
<div class="small text-warm">Sport</div> <!-- route types + " N pitches" if multi -->
</a>
<div class="small text-warm">
<a href=".../area/105744222/boulder-canyon">Boulder Canyon</a> > ... >
<a href=".../area/.../high-energy-area">High Energy Area</a>
</div>
</td>
</tr>
id — /route/(\d+)/name — <strong>([^<]+)</strong> (HTML-entity-decode)grade — text in <span class='rateYDS'> (or rateV, rateWI, etc. depending on type). The same row carries alternate-system equivalents (rateFrench, rateEwbanks, rateUIAA, rateZA, rateBritish) — capture them if the caller wants cross-system grades.star_rating_integer — count of starBlue.svg images. Add 0.5 per starHalfBlue.svg. Note this is rounded (Avg Stars=3.9 renders as 4 full blue stars, no half) — for precise float values, prefer the CSV or the route-detail JSON-LD (step 5).vote_count — <span class="text-muted small"> (\d+)</span> after the stars block. This field is NOT in the CSV.type — text in the inner <div class="small text-warm"> directly inside the anchor (e.g. Sport, Trad, TR, Trad, Sport 2 pitches).pitches — the trailing N pitches text in the same div, when present. Single-pitch routes omit it.area_path — the outer <div class="small text-warm"> after the </a>, which lists the breadcrumb as a sequence of <a href=".../area/..."> anchors separated by >. Note: this breadcrumb is rendered most-general-first (opposite of the CSV's Location column)./route/<id>/<slug> (for first_ascent, precise rating, exact lat/lng)The route detail page is the canonical source for first_ascent (not in CSV, not in row), precise star_rating float, exact GPS, and any other detail field. Server-rendered HTML — works cookieless.
Two extraction surfaces:
(a) Inline JSON-LD (most reliable for rating/lat/lng):
<script type="application/ld+json">
{
"@type": "LocalBusiness",
"name": "Animal Magnetism",
"description": "5.11c Sport",
"geo": { "@type": "GeoCoordinates", "latitude": "39.9984566", "longitude": "-105.41396584" },
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "3.8", "reviewCount": "467" }
}
</script>
(b) description-details table for the typed fields:
Type: Trad, 350 ft (106 m), 5 pitches | Fixed Hardware (2)
GPS: 39.93083, -105.28315
FA: US Army climbers, 1954. FFA: Stan Shepard, Allen Bergen, 1957
Type: as a comma-separated list. The first 1–N elements (until the first match of ^\d+\s*ft) are route types; the remainder is <length> ft (<m> m), <N> pitches for multi-pitch, or <length> ft (<m> m) only for single-pitch. Trailing pipe-separated tags (Fixed Hardware (2)) are gear notes — capture as a separate gear_notes field if the caller wants them.FA: (first ascent) is free-form text — preserve as-is. May include FFA: (first free ascent) for routes initially aided. Routes without recorded FA simply omit the row.area_path — use it to override step 3/4's parsed path when discrepancies arise.page=N with N > 20 (i.e. > 1000 results), the CSV will be truncated to popularity-sorted top 1000. Document the truncation in the response: "truncated": true, "max_csv_rows": 1000.&page=1 through &page=N (50 per page). Total wall cost: ~ceil(N/50) HTTP round-trips at ~1 s each through the residential proxy.is_trad_climb=1 or is_sport_climb=1); dedupe by id if you union filters client-side./data/get-routes and /data/get-routes-for-lat-lon are auth-gated. Cookieless calls return 403 {"success":0,"message":"Invalid Key"} even with a residential-proxy session. The apiKey is only obtainable from a logged-in user profile under /data/. Don't try to use the JSON data API in a stateless agent — verified 2026-05-18 with both ?routeIds=… and ?key=test. The CSV export gives ≥ 90% of the same fields (no first_ascent, no vote_count, but everything else) and is unauthenticated.diffMinrock=2600&diffMaxrock=5500 (YDS 5.10a–5.11d) only filters when type=rock. The diffMin* / diffMax* selects are mutually exclusive in the UI — only the one matching type= is honored; others are ignored. If a caller asks for "5.10 trad OR V4 boulder," issue two separate route-finder queries and merge.type= only has 5 values — rock, boulder, aid, ice, mixed. The task brief's "trad / sport / top rope / alpine / snow / chossaneering" are NOT first-class types. Trad/sport/TR are rock sub-filters (is_trad_climb/is_sport_climb/is_top_rope); alpine/snow/chossaneering are uncategorized free-form tags on individual routes — to find them, scan the Route Type column post-fetch and substring-match on Aid/Alpine/Snow/Ice.is_*) are OR-combined, not AND-combined. A route tagged Trad, TR will appear when is_trad_climb=1 even with is_sport_climb=0,is_top_rope=0, AND when is_top_rope=1 with is_trad_climb=0. Verified 2026-05-18 with a Boulder Canyon query: "Dementia" (Trad, TR) showed up under both filters. The route-finder shows a route if ANY of its disciplines is checked.stars= is bucketed, not continuous. Only these 6 buckets are accepted: 0 | 1.8 | 2.3 | 2.8 | 3.3 | 3.8 (corresponding to "1+", "1.5+", "2+", "2.5+", "3+ of 4 stars"). You can't pass stars=2.5 — the form will silently coerce. If the caller wants a finer threshold (e.g. ≥ 3.4 stars), set stars=2.8 and filter client-side using the precise Avg Stars from the CSV.min_votes filter exists in the form. Filtering by vote count is purely client-side — gate on the vote_count you parsed from each row (HTML path only — CSV doesn't include vote_count).sort1= only takes area | rating | popularity desc | title. rating is ascending (easiest first). Appending desc (sort1=rating desc) is silently ignored — the result set falls through to popularity order. For "hardest first," fetch with sort1=rating and reverse client-side.viewAll=1 is a documented form param but returns 500 Internal Server Error cookieless (verified 2026-05-18 via residential-proxy fetch). It works in a real logged-in browser. Don't pass it from cookieless fetches; paginate with &page=N instead.pp, perPage, routesPerPage, count, limit are all silently ignored.200 OK + empty result list. Mountain Project does not emit a 404 or error for over-paged URLs — always check rows.length > 0 before claiming the page exists.page=N value on the last-page anchor in the pagination block. Compute total_count ≈ N × 50 + (rows on page N).Avg Stars=3.9 shows 4 blue stars in the row. Always cross-check with the CSV's Avg Stars column (precise float) or the JSON-LD aggregateRating.ratingValue on the detail page when precision matters.area_path direction is opposite between CSV and HTML. CSV Location column lists most-specific-first (Bastille > Eldorado Canyon SP > Boulder > Colorado); the HTML breadcrumb on both the result row and the detail page lists most-general-first (Colorado > Boulder > Eldorado Canyon SP > Bastille). Reverse the CSV value if you want canonical "USA → state → ... → crag" order.q= only. Verified 2026-05-18: term=, search=, query=, s= all return {"categories":[],"results":[]}. The internal autocomplete returns a JSON wrapper { "totalResults": <N>, "html": "..." } with HTML embedded — parse the html field, don't expect a clean structured list.totalResults field reports the underlying total (in the 10000s) but you cannot paginate the suggestions endpoint. For deeper search use /route-finder with grade/type filters or visit /search?q=… directly (note: /search is JS-rendered — its inline data-props JSON exposes the configured search facets but the actual result list is loaded by JS and not visible to cookieless fetches).mountainproject.com/route-finder/<id> is NOT bookmark-stable in the way the prompt hypothesized. Mountain Project's URLs are /route-finder?<query> not /route-finder/<id>. The full filter set IS encoded in the URL (every param shown above), so any submitted route-finder URL is a complete shareable bookmark — but there's no compact numeric "filter set ID."browse cloud fetch --proxies) is the difference between consistent 200 OK and intermittent 403s/Akamai-style blocks. Stealth (--verified) is recommended; cookieless requests through a clean residential IP have been working consistently across both iters. Keep aggregate request rate ≤ 1 / second to stay under whatever throttle MP runs.connect.*.browserbase.com, blocking WebSocket-driven page rendering. All evidence in this skill comes from browse cloud fetch (HTTPS to api.browserbase.com, which IS reachable) — page-driving via browse open/browse screenshot is not available. The included PNG screenshots are schematic illustrations of fetched HTML/JSON responses, not photographic captures; they identify selectors and param names accurately. Future runs in a sandbox with full Browserbase WS connectivity should re-capture real screenshots.Top-level envelope (always returned, even on zero matches):
{
"success": true,
"total_count": 312,
"total_count_method": "last_page_link",
"page": 1,
"per_page": 50,
"applied_filters": {
"area": "Boulder Canyon",
"area_id": 105744222,
"route_type": ["sport"],
"grade_min": "5.10a",
"grade_max": "5.11d",
"grade_system": "YDS",
"min_star_rating": 2.8,
"min_votes": 20,
"pitches": "any",
"sort": "popularity",
"_raw_query": "selectedIds=105744222&type=rock&diffMinrock=2600&diffMaxrock=5500&is_trad_climb=0&is_sport_climb=1&is_top_rope=0&stars=2.8&pitches=0&sort1=popularity+desc&sort2=rating"
},
"truncated": false,
"routes": [ /* see per-route shape */ ],
"error_reasoning": null
}
Per-route shape (one entry per result):
{
"id": "105753505",
"name": "Animal Magnetism",
"url": "https://www.mountainproject.com/route/105753505/animal-magnetism",
"grade": "5.11c",
"grade_system": "YDS",
"grade_alts": { "French": "7a+", "Ewbanks": "25", "UIAA": "VIII", "ZA": "26", "British": "E5 6a" },
"type": ["Sport"],
"pitches": 1,
"length_ft": 110,
"star_rating": 3.8,
"star_rating_method": "csv_avg_stars | jsonld_aggregateRating | html_row_rounded",
"vote_count": 467,
"area_path": ["Colorado", "Boulder", "Boulder Canyon", "Cob Rock Area", "High Energy Area"],
"lat": 39.9984566,
"lng": -
Prerequisites
Time Estimate
15-45 minutes depending on use case complexity
Steps
Common Pitfalls
✓ Do
✗ Don't
💡 Pro Tips
✓ Use when
Use when skill capabilities match your task, clear ROI on time saved, and you can validate outputs. Best for repetitive tasks, learning, and quality improvement.
✗ Avoid when
Avoid when task requires deep expertise you can't validate, involves sensitive decisions, or when learning process is more valuable than speed of completion.
kostja94/marketing-skills
aaaaqwq/claude-code-skills
agentbay-ai/agentbay-skills
glebis/claude-skills
shopmeskills/mcp
sundial-org/awesome-openclaw-skills
search-routes fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
We added search-routes from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
Registry listing for search-routes matched our evaluation — installs cleanly and behaves as described in the markdown.
search-routes reduced setup friction for our internal harness; good balance of opinion and flexibility.
Registry listing for search-routes matched our evaluation — installs cleanly and behaves as described in the markdown.
search-routes reduced setup friction for our internal harness; good balance of opinion and flexibility.
I recommend search-routes for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
Useful defaults in search-routes — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
We added search-routes from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
search-routes fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
showing 1-10 of 29