Search AllTrails for hiking, biking, running, climbing, backpacking, or paddling trails near a location with the full filter panel surface (activity, difficulty, length, elevation, route type, suitability, traffic, attributes, rating, sort). Returns structured JSON with name, location, coordinates, length/elevation, difficulty, rating, photos, description, dog/kid policy, and region-wide totals. Read-only.
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 alltrails.com/search-trails-dsqvnxCompatible with
Supported agents
| name | search-trails |
| title | AllTrails Trail Search |
| description | >- Search AllTrails for hiking, biking, running, climbing, backpacking, or paddling trails near a location with the full filter panel surface (activity, difficulty, length, elevation, route type, suitability, traffic, attributes, rating, sort). Returns structured JSON with name, location, coordinates, length/elevation, difficulty, rating, photos, description, dog/kid policy, and region-wide totals. Read-only. |
| website | alltrails.com |
| category | outdoors |
| tags | - outdoors - hiking - trails - search - algolia - datadome |
| source | 'browserbase: agent-runtime 2026-05-18' |
| updated | '2026-05-18' |
| recommended_method | api |
| alternative_methods | [] |
| verified | true |
| proxies | true |
Given a location (URL, intent string, place name, bounding box, lat/lng+radius, or trail slug/ID) and any combination of the AllTrails filter panel dimensions (activity, difficulty, length, elevation gain, route type, dog/kid/wheelchair/stroller suitability, traffic, attributes, season, rating, sort), return matching trails as structured JSON — name, location, coordinates, length, elevation, difficulty, route type, average rating + review count, primary + thumbnail photo URLs, description, trail attributes, dog policy, kid policy, canonical AllTrails URL, plus region-wide totals ("16,544 trails in California"). Read-only — never saves a trail, follows a list, logs a completion, or posts a review.
The AllTrails web UI is a Next.js front-end over a public Algolia search index (alltrails_primary_en-US) and a small REST API at /api/alltrails/v2/*. The search index is the same one the site itself queries — credentials are bootstrapped into every page's hydration JSON. Lead with the Algolia path; only fall back to a stealth Browserbase browser session when you need the trail-detail page (description, recent conditions, GPX, full photo carousel), since those endpoints are DataDome-protected and the Algolia hit already contains ~80% of the fields the skill needs.
| Service | App ID | API Key |
|---|---|---|
| Algolia primary | 9IOACG5NHE | a557051fc69f8a3e456db3084df4780e |
| Algolia maps (heatmap geo) | N4IFDT32SC | b44b73be8a8cd9c256e26c7e0a80fc34 |
AllTrails web (/api/alltrails/*) | n/a (key only) | 3p0t5s6b5g4g0e8k3c1j3w7y5c3m4t8i |
All three are public client-side keys (apiKey rather than adminApiKey). They are visible in unauthenticated page source today; re-extract from a fresh homepage fetch if a request starts 401-ing.
| Input shape | What to do |
|---|---|
Full alltrails.com/explore?b_tl_lat=…&b_tl_lng=…&b_br_lat=…&b_br_lng=… | Use the bounding box as insideBoundingBox=<bl_lat>,<bl_lng>,<tr_lat>,<tr_lng> |
alltrails.com/us/{state} or alltrails.com/{slug} | Search Algolia with filters=type:area AND slug:"…"; take the top hit's state_id / ID |
| Place name ("Yosemite National Park", "Marin County", "Joshua Tree") | Search Algolia with query=<name>&filters=type:area&hitsPerPage=1; record the hit's ID (use as associated_area_ids:<id> later), _geoloc.lat/lng, state_id, country_id |
| State name only ("California") | Look up the state's state_id once via query=<state>&filters=type:area, then use filters=type:trail AND state_id=<id> |
lat,lng + radius_m | Use aroundLatLng=<lat>,<lng>&aroundRadius=<meters> (use aroundRadius=all to disable cap) |
| Intent string ("easy hikes near Yosemite") | Parse out place name → resolve as above; treat the modifier ("easy", "dog-friendly") as filter clauses (step 2) |
Trail slug trail/us/california/grant-lakes | Search Algolia with filters=type:trail AND slug:"<slug>", hitsPerPage=1 |
Trail ID 10694011 | Search Algolia with filters=type:trail AND ID=<id>, hitsPerPage=1 |
GET https://9IOACG5NHE-dsn.algolia.net/1/indexes/alltrails_primary_en-US
?x-algolia-application-id=9IOACG5NHE
&x-algolia-api-key=a557051fc69f8a3e456db3084df4780e
&query=<free-text or empty>
&hitsPerPage=<1-100>
&page=<0-based>
&filters=<filter expression — see below>
&numericFilters=<numeric range — see below>
&aroundLatLng=<lat>,<lng> (when scoping by point + radius)
&aroundRadius=<meters | "all"> (default: 1500000m without "all")
&insideBoundingBox=<bl_lat>,<bl_lng>,<tr_lat>,<tr_lng> (when scoping by bbox)
&attributesToRetrieve=<comma-list> (slim down response — see "1MB cap" gotcha)
/1/indexes/{name} accepts the search params as GET query string (the /query sub-path is POST-only). Auth via URL params works — no headers, no auth cookie. Use --proxies with browse cloud fetch if your egress is from a sandbox/datacenter IP; Algolia itself doesn't bot-detect but a clean residential IP avoids any future tightening.
| Prompt filter | Algolia clause |
|---|---|
| Activity = Hiking / Mountain Biking / Trail Running / Walking / Backpacking / Bike Touring / Road Biking / Rock Climbing / Bird Watching / Camping / Cross-Country Skiing / Fishing / Horseback Riding / Off-Road Driving / Paddle Sports / Scenic Driving / Skiing / Snowshoeing / Via Ferrata | activities:hiking / activities:mountain-biking / activities:trail-running / activities:walking / activities:backpacking / activities:bike-touring / activities:road-biking / activities:rock-climbing / activities:birding / activities:camping / activities:cross-country-skiing / activities:fishing / activities:horseback-riding / activities:off-road-driving / activities:paddle-sports / activities:scenic-driving / activities:skiing / activities:snowshoeing / activities:via-ferrata |
| Difficulty = Easy / Moderate / Hard | difficulty:easy / difficulty:moderate / difficulty:hard (a small strenuous tier also exists — fold into hard for prompt-shape outputs unless surfacing the raw label) |
| Length min/max miles | numericFilters=length>=<min_m>,length<=<max_m> where length is stored in meters (multiply input miles × 1609.344) |
| Elevation gain min/max feet | numericFilters=elevation_gain>=<min_m>,elevation_gain<=<max_m> — also meters (input feet × 0.3048). Note: elevation_meters field exists too but is the trailhead's absolute elevation, not gain. |
| Route type = Loop / Out & Back / Point to Point | route_type:L / route_type:O / route_type:P |
| Suitability = Dog-friendly | (features:dogs OR features:dogs-leash) (off-leash or leashed) — note dogs-no is the explicit-deny tag |
| Suitability = Kid-friendly | features:kids |
| Suitability = Stroller-friendly | features:strollers |
| Suitability = Wheelchair-friendly | features:ada (the indexed enum value for wheelchair-accessible) |
| Trail traffic = Light / Moderate / Heavy | visitor_usage:1 / visitor_usage:2 / visitor_usage:3 (note: a 4 bucket exists in raw data — see gotcha — most popular trails are 3/4) |
| Trail attribute = Wildflowers / Wildlife / Waterfall / River / Lake / Forest / Beach / Cave / Hot springs / Historic site / Views | features:wild-flowers / features:wildlife / features:waterfall / features:river / features:lake / features:forest / features:beach / features:cave / features:hot-springs / features:historic-site / features:views |
| Trail attribute = Off-leash dogs | features:dogs |
| Trail attribute = Paved / Partially paved | features:paved / features:partially-paved |
| Trail attribute = Rocky / Muddy | Not indexed as facets. Surface from the per-trail recent reviews payload only (browser fallback). |
| Open year-round vs seasonal | Use filters=is_closed:false to exclude active closures; full season metadata is on the trail detail page only. |
| Min rating | numericFilters=avg_rating>=<n> (decimal stars, 0.0-5.0) |
| Park / area scope | associated_area_ids:<area_id> (a trail can belong to multiple parks) — preferred over area_id because it includes overlapping designations |
| State / country scope | state_id:<id> / country_id:<id> |
Combine clauses with AND / OR and group with parens, URL-encoded. Example:
type:trail
AND state_id=5
AND (features:dogs OR features:dogs-leash)
AND features:kids
AND difficulty:easy
alltrails_primary_en-US has a single replica (_replica) with identical customRanking: [desc(popularity)] — meaning Algolia ranking is the same on both indexes and AllTrails handles sort variants through the query shape, not a separate sort-index per option:
| Sort prompt value | How |
|---|---|
| Most popular (default) | Default index, empty query= — natural ranking |
| Best match | Default index with the user's free-text query=<term> — text relevance bubbles up |
| Closest | Default index + aroundLatLng=<lat>,<lng> (no aroundRadius cap, or aroundRadius=all) — Algolia's geo ranking criterion places nearest first |
| Highest rated | Sort client-side by avg_rating desc on the returned hits, OR add numericFilters=avg_rating>=4.5,num_reviews>=20 to pre-filter |
| Most reviewed | Sort client-side by num_reviews desc, OR numericFilters=num_reviews>=100 to pre-filter |
(See gotcha: there is no dedicated _avg_rating_desc or _num_reviews_desc replica index. Don't search for one — both _alpha and _local are internal staging mirrors, not sort variants.)
page=0 is the first page, hitsPerPage accepts 1-100 (defaults to 20). Returned nbHits is the true total even when capped; nbPages reflects what's paginable (Algolia caps page × hitsPerPage ≤ 1000 by default — beyond that you need bounding-box subdivision to reach trail 1001+).
Algolia returns each trail as a flat JSON object. Map fields like this:
| Output field | Algolia hit field | Notes |
|---|---|---|
trail_id | ID (also surfaces as objectID cast to string) | Integer |
name | name | |
slug | slug | Path segment, e.g. trail/us/california/grant-lakes |
canonical_url | construct: https://www.alltrails.com/{slug} | |
lat, lon | _geoloc.lat, _geoloc.lng | Trailhead, not centroid |
length_miles | length / 1609.344 | length is meters |
elevation_gain_ft | elevation_gain / 0.3048 | elevation_gain is meters |
difficulty | difficulty ∈ easy / moderate / hard / strenuous | |
route_type | route_type ∈ L / O / P | Map to Loop / Out & Back / Point to Point for output |
activity | activities[] | Array — pick primary by first element (usually hiking) |
attributes | features[] | Array (see gotcha for canonical-vs-display mapping) |
average_rating | avg_rating | Decimal 0.0-5.0 |
review_count | num_reviews | Integer (text + non-text combined). num_text_reviews is text-only subset. |
park_name | area_name + area_type | area_type:N = national park, S = state park, F = national forest, C = city park, W = wilderness, R = regional park, etc. |
city, state, country | city_name, state_name, country_name | |
location_label | location_label | Display string like "Yosemite National Park, California" |
traffic | visitor_usage | 1 light / 2 moderate / 3 heavy / 4 extreme — see gotcha |
dog_policy | derive from features[] | dogs → off-leash · dogs-leash → on-leash · dogs-no → not allowed · absent → unknown |
kid_policy | features:kids → kid-friendly; absent → unknown | |
is_closed, is_private_property | direct booleans | |
description | description | Plain text, sometimes empty — trail-detail page is the canonical source |
estimated_duration | estimated_time_to_complete.value + .unit | e.g. {"value":"6.5–7","unit":"hr"} |
distance_miles (from input pin) | derive client-side: haversine((_geoloc.lat,_geoloc.lng), (input.lat,input.lng)) | Algolia doesn't surface it in the body |
total_region_count | top-level nbHits | Capture this for the "1,234 trails in California" panel-total field |
Each trail hit carries profile_photo_data formatted as {photo_id}-{photo_hash} plus featured_photo_ids[] for additional carousel photos. Get a redirect to the actual image via:
GET https://www.alltrails.com/api/alltrails/v2/trails/{trail_id}/photos/{index}
?key=3p0t5s6b5g4g0e8k3c1j3w7y5c3m4t8i
&size=<sm|md|lg|xl|extra_large>
Response is a 302 to https://images.alltrails.com/<base64> where the base64 decodes to an imgix-style edits descriptor pointing at assets.alltrails.com/uploads/photo/image/{id}/{hash}.jpg. Only extra_large actually applies a resize (max 2048×2048 inside fit) — other sizes return the original. For the prompt's output shape:
primary_photo_url → ?index=0&size=extra_large redirect target.thumbnail_url → ?index=0&size=md.photo_urls[] → ?index=1...num_photos-1 (typically up to featured_photo_ids.length).You can also skip the redirect hop and build the imgix URL yourself when bandwidth matters (the JSON edits payload is deterministic; the bucket is always assets.alltrails.com and the key is uploads/photo/image/{photo_id}/{photo_hash}.jpg).
When the prompt mentions a state, country, or area scope, run a hitsPerPage=0 count-only query alongside the main search to populate the "X trails in California" headline number:
GET .../alltrails_primary_en-US?query=&hitsPerPage=0&filters=type:trail AND state_id=5&x-algolia-application-id=…&x-algolia-api-key=…
→ {"hits":[], "nbHits":16544, "nbPages":0, "page":0, ...}
nbHits is the answer (validated 2026-05-18: California = 16,544; Yosemite NP via associated_area_ids:10106838 = 32 easy + 130 surface-level matches).
The Algolia search index is intrinsically read-only — there's no POST /book, POST /save, or POST /complete to call by accident. The /api/alltrails/v2/trails/{id}/photos/{idx} endpoint is GET-only. You cannot accidentally save a trail, follow a list, log a completion, or post a review through this surface. When falling back to the browser path (next section), do not click Save / Add-to-list / Mark-completed / Submit-review buttons.
The trail-detail page, the user-reviews list, the recent-conditions feed, and the GPX download are gated by DataDome on www.alltrails.com. The fetch API path returns 403 with X-Datadome: protected for every /trail/*, /explore*, /parks/*, /us/*, and /api/alltrails/v2/trails/{id} (non-photo) endpoint — confirmed 2026-05-18 across 8 paths. To reach those, you need a Browserbase stealth + residential proxy session that lets DataDome complete its JS challenge:
SID=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r .id)
browse open "https://www.alltrails.com/trail/us/california/grant-lakes" --remote -s "$SID"
browse wait load --remote -s "$SID"
browse wait timeout 4000 --remote -s "$SID" # DataDome JS challenge resolves async
browse get markdown body --remote -s "$SID"
The first navigation after a new session typically lands on a DataDome interstitial; the JS challenge solves within 2-4 s on a stealth+proxy session, after which subsequent same-session fetches return 200 (the datadome=<…> cookie persists for ~6 hours). Once on the trail-detail page, extract:
<section data-testid="trail-description"> text, cleaned of HTML.<section data-testid="recent-conditions">; each row has a date, condition tags (Rocky, Muddy, Snowy, Icy, Overgrown, Fallen tree), and a reviewer handle. This is where rocky and muddy live — they are surfaced as condition tags on reviews, not as Algolia features: facets.<div data-testid="trail-parking">.gpx_url: null, gpx_requires_auth: true, gpx_unlock: "alltrails-pro". When authed (Cookie containing a _at_session_id_v2 + Pro entitlement), the link is /trail/{slug}/gpx?key={web_api_key}.heatmap_available flag.Avoid browse snapshot on the explore/search-results pages — they're virtualized lists with 0 stable a11y refs; iterate via the Algolia path for the listings themselves and use the browser only for per-trail enrichment.
X-Datadome: protected, datadome.alltrails.com script tag, X-Dd-B: 3 header). The mitigation is the same shape — stealth + residential proxy + a few seconds for the JS challenge — but the cookie name (datadome=…) and the captcha host (geo.captcha-delivery.com) differ from Cloudflare's cf_clearance, so don't reuse Cloudflare-specific tooling/fixtures./ is the only AllTrails web path that returns 200 without solving DataDome. Every other URL — /explore, /trail/*, /parks/*, /us/{state}, /api/alltrails/v2/trails/{id} (non-photo) — is 403 on a cold session. The homepage is your "extract bootstrapped credentials, then leave" target if the in-bundle Algolia/web keys need re-discovery.elevation_meters is something else. length and elevation_gain are stored in meters regardless of the units: "i" flag on each hit (the units flag is a display preference for the user, not a data unit). elevation_meters is the trailhead's absolute elevation above sea level, not the gain — don't confuse them. Convert length / 1609.344 for miles, elevation_gain / 0.3048 for feet.route_type is one letter, not a word. L / O / P → Loop / Out & Back / Point to Point. The filter facet uses the letter too (route_type:L).difficulty has FOUR values, not three. The AllTrails filter panel shows three (Easy/Moderate/Hard), but the index also contains strenuous. Treat strenuous as a stricter superset of hard when mapping back to the panel's three-button UI.visitor_usage has FOUR levels, not three. Panel shows Light / Moderate / Heavy (1/2/3) but the data has a fourth bucket (4) that's not exposed in the filter UI but appears in ~94% of popular-trail hits in our 1,000-trail sample. Treat 4 as "Extreme / very crowded" and either fold into Heavy or surface as a separate enum if your output schema allows.browse cloud fetch. The Browserbase Fetch API rejects responses > 1MB. With default attributes, ~250 hits per response is the practical ceiling. For larger pulls, use attributesToRetrieve=[…] to slim each hit to the fields your output needs (a 1,000-hit slim response with 6 attrs comes in at ~825 KB — verified 2026-05-18).page × hitsPerPage caps at 1,000. Algolia returns trail-1 through trail-1000 across pages; trail-1001+ silently disappear even though nbHits is the true total. To exhaustively enumerate a state, subdivide by insideBoundingBox quadrants (lat halves × lng halves recurse until each cell's nbHits ≤ 1000).state_id / country_id, not name strings. state_name is in searchableAttributes (text-search index) but NOT in attributesForFaceting, so filters=state_name:"California" returns 0. Always resolve a state-name input to state_id first via a type:area query, then filter on the numeric ID. (Observed: California = 5, country US = 313.)area_id vs associated_area_ids. A trail's "home" park is area_id; associated_area_ids[] includes overlapping designations (e.g. a trail inside Yosemite NP that also touches Stanislaus National Forest will list both). Always use associated_area_ids:<id> for park-scoped filters — area_id:<id> will miss legitimate hits.views, wildlife, wild-flowers (not wildflowers), forest, river, waterfall, lake, beach, cave, hot-springs, historic-sitedogs (off-leash welcome), dogs-leash (not dogs-leashed), dogs-no (explicit "no dogs")kids (not kid-friendly), strollers, ada (the indexed value for wheelchair-accessible — NOT wheelchair)paved, partially-pavedcity-walk, rails-trails, pub-crawlrocky, muddy, snowy, icy, overgrown, fallen-tree — these are review-time condition tags (live on the trail detail page's Recent Conditions panel), not index facets. The AllTrails filter panel calls them "Trail conditions" and that filter is implemented client-side from the most-recent reviews, not from a server facet.hiking, walking, running (general), trail-running, mountain-biking, road-biking, bike-touring, backpacking, camping, birding (not bird-watching), fishing, horseback-riding, rock-climbing, snowshoeing, skiing, cross-country-skiing, paddle-sports, scenic-driving, off-road-driving, via-ferrata.alltrails_primary_en-US is [typo, geo, words, filters, proximity, attribute, exact, custom] with customRanking: [desc(popularity)]. There is no *_avg_rating_desc or *_num_reviews_desc replica — the only replica is alltrails_primary_en-US_replica with the same ranking. For "Highest rated" / "Most reviewed" sort, fetch the top-N by popularity (or by aroundLatLng for distance) then sort the returned hits client-side. Don't waste a request hunting for a non-existent sort replica.type:trail filter is mandatory. Without it, your search results will be polluted with type:area, type:city, type:park, type:poi, and type:user hits. Always include type:trail for trail-listing tasks; use type:area only when resolving a place-name input to an ID.profile_photo_data looks like a single string but is {photo_id}-{photo_hash}. The hyphen is the field separator. profile_photo_data: "50889290-02bd9fbfaf51cdd737bb00f0198e0fe4" → photo_id = 50889290, photo_hash = 02bd9fbfaf51cdd737bb00f0198e0fe4. featured_photo_ids[] is a parallel array of additional photo IDs for the carousel./api/alltrails/v2/trails/{id}/photos/{idx} is the one DataDome-allowlisted AllTrails-owned endpoint — it 302s straight through (verified 2026-05-18). All other /api/alltrails/v2/* paths are 403'd by DataDome from the same cold session. Don't waste time probing other /api/alltrails/ paths from a non-stealth fetch; they require the same browser-session unblocking as the page routes._alpha and _local Algolia indexes are internal mirrors, not regional variants. alltrails_primary_en-US_alpha is a staging index with ~83% the row count of production; alltrails_primary_en-US_local has 1,902 entries (test fixtures). Always use `alAI-first code editor with Composer
Before installing skills in Cursor, ensure your development environment meets these requirements:
node --versionsearch-trailsExecute the skills CLI command in your project's root directory to begin installation:
browse install alltrails.com/search-trails-dsqvnxFetches search-trails from alltrails.com/search-trails-dsqvnx 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-trails
Restart Cursor to activate search-trails. Access via /search-trailsin 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.
multi-search-engine
aaaaqwq/claude-code-skills · Productivity · tag: search
minimax-web-search
thincher/awsome_skills · Productivity · tag: search
exa-search
affaan-m/everything-claude-code · Productivity · tag: search
extract-listings
realtor.com/extract-listings-9v0y4r · real-estate · tag: search
firecrawl-search
firecrawl/cli · Productivity · tag: search
tavily-search
veithly/tavily-search · Productivity · tag: search
search-trails fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
search-trails is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
Solid pick for teams standardizing on skills: search-trails is focused, and the summary matches what you get after install.
Registry listing for search-trails matched our evaluation — installs cleanly and behaves as described in the markdown.
search-trails has been reliable in day-to-day use. Documentation quality is above average for community skills.
search-trails reduced setup friction for our internal harness; good balance of opinion and flexibility.
I recommend search-trails for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
search-trails fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
Useful defaults in search-trails — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
search-trails has been reliable in day-to-day use. Documentation quality is above average for community skills.
showing 1-10 of 43