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.
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
Time estimate: 15-45 minutes depending on use case complexity
Prerequisites
Submit your Claude Code skill and start earning
browse install mountainproject.com/search-routes-romkbpCompatible with
Supported agents
| 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": -
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:
browse install mountainproject.com/search-routes-romkbpFetches 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:
.cursor/skills/search-routes
Restart Cursor to activate search-routes. Access via /search-routesin your agent's command palette.
Learn more:View source on GitHubSkills CLI docsAbout CursorWhat are agent skills?
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.
google-image-search
glebis/claude-skills · Backend · tag: search
boss-job-search
agentbay-ai/agentbay-skills · Productivity · tag: search
google-gemini-file-search
jezweb/claude-skills · Backend · tag: search
web-search
claude-office-skills/skills · Productivity · tag: search
search-patents
patents.google.com/search-patents-369445 · research · tag: search
search-listings
depop.com/search-listings-p8xawn · marketplace · tag: search
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