AllTrails Hiking Scraper - Trails, Stats & Reviews
Pricing
from $1.20 / 1,000 trail records
AllTrails Hiking Scraper - Trails, Stats & Reviews
Scrape AllTrails, the world's largest hiking platform: trails with difficulty, length, elevation gain, route type, ratings, review counts, dog and kid friendly attributes, trail conditions and hiker reviews. Browse any region or paste links, with recurring change monitoring.
Pricing
from $1.20 / 1,000 trail records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
3
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
AllTrails Hiking Scraper
Collect public AllTrails trail records from regional grids, tag grids, or trail links. Listing mode returns core trail identity, location, ratings, difficulty, distance, elevation, and route information when the site serves it. Detail mode adds descriptions, conditions, attributes, rating breakdowns, counts, and recent reviews. The actor also supports photos, readable GPS-track files, resume, incremental monitoring, and optional connector delivery.
Why This Scraper?
- Full trail rows. Collects trail identity, location, difficulty, ratings, distance, elevation, route type, activities, and features in one dataset row.
- Flexible scope. Supports regional browsing, tag-specific grids, pasted grid links, and pasted trail links.
- GPS tracks. Reads the site's served GPS track as an encoded polyline with a point count when detail mode provides it.
- Optional depth. Adds descriptions, alerts, highlights, review samples, weather, and popularity when detail reading is on, and community photos independently, on by default.
- Consistent filters. Applies difficulty, rating, review-count, activity, route-type, length, elevation, and feature filters consistently in both collection modes.
- Resume and monitoring. Supports resume for interrupted runs and incremental labels for recurring monitoring.
- Your data stays in Apify. Keeps the complete dataset in Apify and can optionally export a concise summary through an authorized connector.
Use Cases
- Outdoor trip planning apps and route pickers: pull trail difficulty, distance, elevation gain, route type and the GPS track to power a map-based planner.
- Land manager and park communications teams: watch a park's or region's trails for new alerts, closures and highlights posted on the detail page.
- Outdoor content and gear marketing teams: build region guides from ratings, review counts, popularity and season windows without opening every trail page by hand.
- Trail recommendation and outdoor data science: assemble a dataset of length, elevation, difficulty, activities and features across a region for a recommendation model.
- Hiking clubs and community monitoring: schedule recurring updates to catch newly listed trails, rating changes or condition changes for a watched region.
Data You Get
Sample shape: values are illustrative placeholders, not from a live trail.
| Field | Example |
|---|---|
recordType | trail |
id | 00000001 |
name | Sample Lake Loop Trail |
url | https://www.alltrails.com/explore/trail/sample-lake-loop-trail |
slug | trail/us/colorado/sample-lake-loop-trail |
difficulty / difficultyGrade | moderate / 3 |
rating / ratingCount | 4.8 / 1200 |
lengthMeters / lengthMiles | 8500 / 5.3 |
elevationGainMeters / elevationMaxMeters | 420 / 3120 |
durationMinutes / routeType | 180 / Loop |
activities / features | ["hiking"] / ["lake"] |
areaName / stateName | Sample National Park / Colorado |
cityName / countryName | Sample Valley / United States |
lat / lng | 39.0000 / -106.7000 |
trailUrl / avgRating / numReviews | same as url / rating / ratingCount (alias columns) |
latitude / longitude | same as lat / lng (alias columns) |
elevationGainFt / profilePhotoUrl | 1378 / same as imageUrl |
dogFriendly / kidFriendly | true / true (from the trail's features; the site's no-dogs feature gives false) |
isClosed / visitorUsage | false / 3 (closure flag from the listing or a closure alert; usage level 1 to 4) |
routeGeometry / routePoints | encoded-polyline-placeholder / 000 |
overview / description | Sample overview / Sample description text |
alerts / highlights | [] / [] |
reviews / photos | [] / [] |
surfaceTypes / weather | ["dirt"] / {"summary":"Sample forecast"} |
sourceUrl / scrapedAt | https://www.alltrails.com/us/colorado / 2026-01-01T00:00:00.000Z |
Listing fields are populated whenever the source supplies them. Detail fields depend on fetchDetails for a region or tag grid row; a row built from a pasted trail link always reads its own detail page, so its detail fields are populated either way. The detail page serves the five most recent reviews, five per trail, in the reviews field. routeGeometry and routePoints are present only when the site serves a GPS track. photos depends on fetchPhotos and the photo service response, independently of fetchDetails.
How to Use
Browse one geographic region with the default listing fields. Type a place or region keyword such as California, Yosemite, or British Columbia into regionPath. The actor resolves the best geographic match on AllTrails, then builds the region and tag-grid URLs. By contrast, My own links mode accepts a list of separate grid or trail links.
Search by keyword:
{"mode": "search","regionPath": "Colorado","maxItems": 20}
Browse with tags, filters and details:
{"mode": "search","regionPath": "Colorado","tags": ["backpacking", "waterfall"],"difficulty": ["moderate", "hard"],"minRating": 4.5,"fetchDetails": true,"maxItems": 50}
Scrape pasted links:
{"mode": "urls","urls": ["https://www.alltrails.com/us/california","https://www.alltrails.com/explore/trail/us/colorado/sample-lake-loop-trail"],"fetchDetails": true,"maxItems": 10}
A pasted trail link still produces a URL-backed row if the detail page is temporarily refused; detail-only fields remain empty and no detail surcharge is charged. A genuine missing trail page is omitted.
A pasted trail link has no listing card to fall back on, so its record always comes from reading that trail's own detail page, whether or not fetchDetails is on; the detail surcharge applies whenever that read succeeds and the row is returned. fetchDetails instead decides whether region and tag grid rows, in either mode, are enriched with the same detail fields.
Monitor a region on a schedule:
{"mode": "urls","urls": ["https://www.alltrails.com/us/colorado"],"fetchDetails": true,"storeTracksInKV": true,"incrementalMode": true,"stateKey": "sample-colorado-monitor"}
Use fetchDetails: true when you need descriptions, reviews, detail attributes, or GPS tracks for region and tag grid rows. The dataset row is always the primary output. storeTracksInKV adds a readable key such as track__us-colorado__sample-lake-loop-trail.json for systems that prefer one track object per trail.
Link shapes in urls mode
| Link shape | Example | Behaviour | Covered by example link |
|---|---|---|---|
| Region or country grid | /us/california, /us | Reads the grid's ten most popular trails | itself (two example links) |
| Tag grid | /us/california/backpacking | Same, for that tag | itself |
| City grid | /us/california/san-francisco | Same, for that city | itself |
| Park grid | /parks/us/california/yosemite-national-park | Same, for that park | itself |
| Trail page | /trail/us/california/half-dome-via-the-john-muir-trail-jmt | One trail record | itself |
| Legacy trail slug | /trail/us/california/half-dome-trail | Redirects to the canonical trail; the same trail pasted twice is returned once | the trail page |
| Non-www host | https://alltrails.com/us/california | Treated as the www link | the region grid |
| Tracking query | ...?ref=share | Query is ignored | the link without the query |
?page=2, ?sort=newest | /us/california?page=2 | Ignored with a warning: the grid is read from its first page. Because the link names a slice that is not read, a run with such a link never marks records as gone | the same grid without the query |
/explore/trail/... | /explore/trail/us/california/half-dome-trail | Accepted as a trail link | the trail page |
Behaviour change: with mode set to urls and the urls field left untouched (absent), the run now scrapes the example set above (six links, each a different target) instead of failing. The legacy slug, non-www and tracking-query variants above are accepted but are not in the example set, because each resolves to a target already listed there. A live check found that ?page=2 and ?sort=newest return the same ten trails as the plain grid, so they are not separate shapes either. An explicitly empty list still fails. tags has no default and is only read in region mode; the Console pre-fills backpacking as an example.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/alltrails-hiking-scraper").call(run_input={"mode": "search", "regionPath": "Colorado", "maxItems": 20})for trail in client.dataset(run["defaultDatasetId"]).iterate_items():print(trail["name"], trail["difficulty"], trail["rating"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/alltrails-hiking-scraper').call({ mode: 'search', regionPath: 'Colorado', maxItems: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Send results into your apps (MCP connectors)
Add authorized connector IDs to mcpConnectors to send concise summaries after the scrape. Set notionParentPageUrl when using Notion. Connector delivery is optional and never changes the complete Apify dataset.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | Choose search to browse a region or urls to read the links in urls. |
regionPath | string | California | Place or region keyword resolved to a geographic AllTrails scope in search mode. Existing paths and full AllTrails URLs remain accepted for compatibility. |
tags | string[] | ["backpacking"] | Optional tag grids to add after the region grid. An unavailable tag produces a clear validation failure. |
latitude | number | empty | Search mode only: latitude of a point to collect trails around instead of a region. Needs longitude too. Region and tags are ignored while a point or box is set. Lists up to 1,000 trails per area, nearest the point first; a larger area is cut at 1,000, and a cut area skips gone-record detection. |
longitude | number | empty | Search mode only: longitude of the point; needs latitude too. |
searchRadius | number | 50 | Radius in miles around latitude / longitude, 1 to 500. |
customBottomLat | number | empty | Search mode only: south edge of a bounding box. All four box fields are required together and the box wins over the point. |
customBottomLng | number | empty | Search mode only: west edge of the bounding box. |
customTopLat | number | empty | Search mode only: north edge of the bounding box. |
customTopLng | number | empty | Search mode only: east edge of the bounding box. |
urls | string[] | example set (see below) | Grid or trail links used by urls mode. Trail links produce one enriched trail record. |
followSubGrids | boolean | false | In urls mode, follow additional grid links discovered from pasted grid pages, which is read in that mode only. |
fetchDetails | boolean | false | Open each region or tag grid trail's detail page for descriptions, exact attributes, conditions, reviews, and any GPS track served by the page. A detail charge applies to successfully saved detail rows. A pasted trail link always reads its own detail page regardless of this setting, since it has no grid card to fall back on. |
fetchPhotos | boolean | true | Fetch community photos from the separate photo service, independently of fetchDetails. A failure keeps the trail row and does not fail the run. |
maxPhotos | integer | 10 | Maximum photos per trail when enabled. Range: 1 to 120. |
storeTracksInKV | boolean | false | Save each served GPS track as a readable per-trail KV record such as track__us-colorado__sample-lake-loop-trail.json, in addition to the dataset row. A storage charge applies only after the KV write succeeds. |
difficulty | string[] | empty | Keep only trails labelled easy, moderate, hard, or strenuous (expert is accepted as a synonym of strenuous). Applied to records collected in either mode. |
visitorUsageFilter | string[] | empty | Keep trails by crowd level: light, moderate or heavy, from the site's 1 to 4 usage scale (light 1-2, moderate 3, heavy 4). Only listing rows carry the level, so a pasted trail link is excluded when this is set. |
minRating | number | 0 | Keep trails whose rating is at least this value. Range: 0 to 5. |
minReviews | integer | 0 | Keep trails with at least this many reviews. |
activityFilter | string[] | empty | Keep trails tagged with at least one activity such as hiking, backpacking, or mountain-biking. |
routeTypeFilter | string[] | empty | Keep loop, out-and-back, or point-to-point routes. |
lengthMin | number | empty | Keep trails at least this long, in metres. |
lengthMax | number | empty | Keep trails no longer than this, in metres. |
elevationGainMin | number | empty | Keep trails with at least this elevation gain, in metres. |
elevationGainMax | number | empty | Keep trails with no more than this elevation gain, in metres. |
featuresFilter | string[] | empty | Keep trails containing every selected feature label, such as waterfall, views, forest, or dogs-leash. |
maxItems | integer | 20 | Maximum trail rows across the run. 0 means no item cap. |
maxPages | integer | 0 | Maximum grid pages as a runaway guard. 0 reads until the configured scope ends, and any run is capped at 200 grid pages to bound compute. |
resumeFromRunId | string | empty | Continue one interrupted run or dataset. Previously returned trail IDs are skipped. Do not combine this with incremental monitoring. |
incrementalMode | boolean | false | Remember the same scope for recurring runs. The first run returns NEW; later runs return changes by default. |
stateKey | string | empty | Optional name for the incremental campaign. Empty derives a stable key from the collection scope. |
emitUnchanged | boolean | false | In incremental mode, also return unchanged trails labelled UNCHANGED. These rows are returned and billed. |
emitExpired | boolean | false | In incremental mode, return trails absent from a complete scan as EXPIRED. Capped or interrupted scans do not infer expiration. |
ignoreFieldsForChanges | string[] | empty | Incremental mode only. Extra field names to leave out of change detection, on top of the built-in exclusions. A dotted name such as parent.child reaches inside an object or list of objects. Ignored fields stay in every returned row. |
mcpConnectors | string[] | empty | Optional authorized connector IDs for concise result summaries after the scrape. |
notionParentPageUrl | string | empty | Notion destination page when a Notion connector is selected. Ignored by other connectors. |
maxNotifyListings | integer | 50 | Maximum rows sent to each connector. Does not change the dataset. |
proxyConfiguration | object | Apify connection | Connection settings for source requests. Keep the prefilled setting unless you have an authorized alternative. |
Resume and incremental monitoring are separate features. Resume continues one interrupted collection. Incremental mode compares recurring runs of the same scope and adds changeType, changedFields, firstSeenAt, and lastSeenAt to returned rows.
What counts as a change
A trail is labelled UPDATED when its name, difficulty, length, elevation, route type, location, features, activities, description, alerts, highlights or season changed; changedFields names those fields. These fields move on their own between two reads of an unchanged trail, so they never decide UPDATED and never appear in changedFields, although they stay in every returned row: rating, ratingCount, reviewCount, reviewTextCount, photoCount, completedCount, popularity, ratingsBreakdown, reviews, reviewsFetched, imageUrl, photos, weather, scrapedAt, sourceType, sourceUrl and position. Use ignoreFieldsForChanges to exclude more fields, for example a nested key written as parent.child. Dotted names do not reach inside description, overview or alerts, which are compared as a whole. Changing the ignore set never creates a wave of UPDATED rows: the saved baseline is re-compared under the current set.
Output Example
Sample shape: values are illustrative placeholders, not from a live trail.
{"recordType": "trail","id": "00000001","name": "Sample Lake Loop Trail","url": "https://www.alltrails.com/explore/trail/sample-lake-loop-trail","slug": "trail/us/colorado/sample-lake-loop-trail","difficulty": "moderate","difficultyGrade": 3,"rating": 4.8,"ratingCount": 1200,"reviewTextCount": 300,"lengthMeters": 8500,"lengthMiles": 5.3,"elevationGainMeters": 420,"elevationMaxMeters": 3120,"durationMinutes": 180,"routeType": "Loop","activities": ["hiking"],"features": ["lake", "views"],"areaName": "Sample National Park","areaSlug": "parks/us/colorado/sample-national-park","cityName": "Sample Valley","stateName": "Colorado","countryName": "United States","lat": 39.0000,"lng": -106.7000,"routeGeometry": "encoded-polyline-placeholder","routePoints": 0,"imageUrl": "https://images.alltrails.com/000000000-placeholder","overview": "Sample trail overview.","description": "Sample detail description text appears when fetchDetails is enabled.","alerts": [],"highlights": [],"ratingsBreakdown": {"1": 10, "2": 20, "3": 80, "4": 300, "5": 790},"reviewCount": 300,"photoCount": 40,"completedCount": 8000,"seasonStart": "2026-05-01","seasonEnd": "2026-10-01","reviews": [],"photos": [],"surfaceTypes": ["dirt"],"weather": {"summary": "Sample forecast"},"popularity": 0.75,"reviewsFetched": 5,"sourceType": "grid","sourceUrl": "https://www.alltrails.com/us/colorado","position": 1,"scrapedAt": "2026-01-01T00:00:00.000Z","changeType": "NEW","changedFields": [],"firstSeenAt": "2026-01-01T00:00:00.000Z","lastSeenAt": "2026-01-01T00:00:00.000Z"}
Plan Requirement
AllTrails protects its pages with an anti-bot check that refuses plain datacenter and direct connections, so this actor needs residential proxy access, which Apify paid plans include. Apify free plans do not include residential proxies. The actor tries the residential connection first and falls back to a backup connection when it is unavailable; on a free plan it logs "The selected connection is unavailable to this run" and continues on the backup, where some pages may still be refused. If no page can be read the run stops with a clear message: either "The proxy connection was refused for this account ... a paid plan is required", "No results: residential proxies are unavailable on this run ...", or "Could not load any results page (the requests failed) ... This is a connection problem, not an empty search." You can also supply your own proxy URLs in the proxy setting.
FAQ
How much does it cost?
You pay for four kinds of events: a start fee per run (charged per GB of run memory, so the default memory setting means several start events), a fee per trail record returned, a detail enrichment fee for each returned trail whose detail page was read (the first reviews are included in that fee), and a GPS track storage fee for each track written to the key value store. Failed, empty or unchanged-and-suppressed rows are not charged. The Pricing tab on the actor page shows the current rates. Use Maximum trail records to cap the cost of any run.
Is it legal to scrape AllTrails?
This actor collects only publicly available trail listings and detail pages. You are responsible for how you use the data: follow AllTrails' terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Trail statistics such as length and elevation are generally facts, but descriptions, photos and reviews may carry their own rights.
Which dataset views and review limits exist?
The dataset has four views: Results, Trail detail, Reviews and Changes. The detail page serves the five most recent reviews per trail, so reviews holds at most five entries. A grid page holds ten trails, so each link or tag reads its ten most popular trails; maxPages is capped at 200 grid pages per run.
Can I get only new or changed trails on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Recurring updates. Each run then returns NEW, UPDATED and REAPPEARED trails by default, and unchanged trails are not billed unless you turn on Return unchanged trails. With Return expired trails on, a trail is marked EXPIRED only after a complete scan no longer lists it in the grids you read; a capped or resumed run, or one where a grid page was refused or left unread, or a point or box search matched more than 1,000 trails, skips gone-record detection. One exception to know about: if a pasted trail link's detail page is refused on a later run, that run returns a URL-backed row for the link and skips gone-record detection entirely, so no trail is marked EXPIRED on its account; re-run it once the page reads again.
Why does fetchDetails not seem to change a pasted trail link?
A pasted trail link has no listing card to fall back on, so its record always comes from reading that trail's own detail page, description, reviews and GPS track included, and the detail surcharge applies whenever that read succeeds and the row is returned, whether or not fetchDetails is on. The toggle instead controls whether region and tag grid rows, in either mode, are enriched with those same detail fields.
Why did my run fail instead of returning an empty dataset?
If the site refuses every grid page, the run stops with a clear message so "no trails match" is never confused with "nothing could be read". Run it again in a few minutes. If only some grids could be read, the run still succeeds and reports the trails it did collect. A refused pasted trail link does not fail the run: it returns a URL-backed row with detail fields empty and no detail surcharge.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push trail summaries into Notion, Linear, Airtable or another authorized app.
๐ More scrapers you might like
Pair this actor with these related scrapers from the same team:
| ๐ YesStyle Scraper Scrape YesStyle products by keyword or URL. Extract prices, discounts, ratings, stock... | ๐งฉ KKday Scraper Scrape KKday travel activities by keyword, city or URL. Every row carries prices... |
| ๐จ Hipcamp Scraper Scrape Hipcamp campgrounds, RV parks, glamping and unique stays by US or AU region or... | ๐งฉ TikTok Shop Scraper Scrape TikTok Shop by keyword, category or product URL. Extract prices, discounts, sales... |
| ๐ Lennar Scraper Scrape Lennar (lennar.com) new-home communities, move-in-ready homes and floor plans by... | โ๏ธ Viator Scraper Collect Viator.com tour and activity results from destinations, categories, search pages... |
๐ Browse all abotapi scrapers
๐ฌ Support
- ๐ Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- ๐ ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com.
- โญ Enjoying it? A quick review on the actor page helps other users find it.