Checks Recreation.gov daily for permit availability — across one or more facilities — on your available dates and emails you when spots open up.
A GitHub Action runs daily in the early morning Pacific time (twice, at ~6 and ~7 AM, to cover the PST/PDT shift). It reads your facilities, dates, and target trailheads from config.yml, queries the Recreation.gov API, and sends you an email with a direct booking link if any permits are available. If every request fails (e.g. recreation.gov blocks the checker), the job exits non-zero and emails you a failure alert.
Edit config.yml. Each entry under facilities is a recreation.gov facility
with its own facility_id, dates, and trailheads:
facilities:
- name: "Central Cascades Wilderness"
facility_id: "300009"
dates:
- "2026-07-04"
- "2026-07-12"
trailheads:
- name: "Green Lakes / Soda Creek"
tour_id: "2003"
url: "https://www.recreation.gov/ticket/300009/ticket/2003"
- name: "Devils Lake / South Sister"
tour_id: "10088687"
url: "https://www.recreation.gov/ticket/300009/ticket/10088687"
- name: "Enchantment Permit Area"
facility_id: "233273"
dates:
- "2026-08-01"
trailheads:
- name: "Snow Lakes"
tour_id: "4675311"
url: "https://www.recreation.gov/ticket/233273/ticket/4675311"The repo ships with the Central Cascades facility and all its trailheads pre-populated — trim or add as needed.
Find the facility on Recreation.gov. Its id is the number in the URL
(.../ticket/300009/... → "300009"). Add a new entry under facilities
with that facility_id, the dates you want to check, and its trailheads.
By default every trailhead is checked against its facility's dates. To watch a
trailhead only on certain dates, give it its own dates list:
trailheads:
- name: "Devils Lake / South Sister"
tour_id: "10088687"
url: "https://www.recreation.gov/ticket/300009/ticket/10088687"
dates: # overrides the facility dates for this trailhead only
- "2026-07-04"| Field | Level | Required | Default |
|---|---|---|---|
facility_id |
facility | Yes | — |
name |
facility | No | falls back to facility_id in log output |
dates |
facility | No* | empty — see note below |
trailheads |
facility | Yes | — |
name, tour_id, url |
trailhead | Yes | — |
dates |
trailhead | No | inherits the facility's dates |
* A facility's dates is only optional if every trailhead under it defines
its own dates. If a facility has no dates and a trailhead doesn't either,
that trailhead is checked against an empty date list — i.e. nothing happens
for it (no API call, no error). Always set dates at one level or the other.
facilities covers day-use permits (recreation.gov's ticket API).
Overnight permits use a different API and go under a separate
overnight_permits section, keyed by permit_id and division_id instead of
facility_id and tour_id:
overnight_permits:
- name: "Central Cascades Wilderness Overnight"
permit_id: "4675311" # from .../permits/4675311
dates:
- "2026-07-04"
trailheads:
- name: "Devils Lake / South Sister (overnight)"
division_id: "467531103" # from the permit's availability grid
url: "https://www.recreation.gov/permits/4675311/registration/detailed-availability"
- name: "Green Lake / Soda Creek (overnight)"
division_id: "467531108"
url: "https://www.recreation.gov/permits/4675311/registration/detailed-availability"Both sections are optional and can be used together; hits from either land in the
same email. dates and per-trailhead dates overrides work identically. Give
day-use and overnight entries distinct names — a name reused across (or within)
sections merges their results and logs a warning.
recreation.gov has no per-division deep link, so url is the same for every
division under a permit: use .../permits/<permit_id>/registration/detailed-availability.
The email appends ?date=<date>, landing you on that date's availability grid
where you click the division's cell.
| Field | Level | Required | Default |
|---|---|---|---|
permit_id |
permit | Yes | — |
name |
permit | No | falls back to permit_id in log output |
dates |
permit | No* | empty — same rule as facilities |
trailheads |
permit | Yes | — |
name, division_id, url |
trailhead | Yes | — |
dates |
trailhead | No | inherits the permit's dates |
In your repo go to Settings → Secrets and variables → Actions and add:
| Secret | Required | Value |
|---|---|---|
GMAIL_ADDRESS |
Yes | Your Gmail address (used to send the mail) |
GMAIL_APP_PASSWORD |
Yes | 16-character app password from myaccount.google.com/apppasswords |
NOTIFY_EMAIL |
No | Where to send alerts. Defaults to GMAIL_ADDRESS if unset. |
Go to Actions → Check Permit Availability → Run workflow to run it immediately and confirm the email works before waiting for the daily schedule.
Find the trailhead on Recreation.gov. The URL will look like:
https://www.recreation.gov/ticket/{facility_id}/ticket/{tour_id}
Copy the tour_id from the URL and add a new entry under the facility's
trailheads list in config.yml.
pip install -r requirements.txt
GMAIL_ADDRESS=you@gmail.com GMAIL_APP_PASSWORD=xxxx NOTIFY_EMAIL=you@gmail.com python check_permits.pypip install -r requirements-dev.txt
python -m pytest tests/ -vThis repo uses ruff and pre-commit. After installing dev dependencies:
ruff check . # lint
ruff format . # format
pre-commit install # run the hooks automatically on every commit