find-class▌
classpass.com/find-class-uoiq0m · updated May 21, 2026
MDX-style export adds YAML metadata + attribution linking explainx.ai and this canonical listing URL.
Search ClassPass for available fitness, wellness, beauty, or recovery class slots near a location and return matching results as structured JSON (class id, instructor, venue, start/end time in tz, credit cost, premium flag, modality, spots, amenities, rating). Accepts free-form intent, ZIP/city + category + date, a direct /search URL, or a venue slug. Read-only — never books.
| name | find-class |
| title | ClassPass Find Class |
| description | >- Search ClassPass for available fitness, wellness, beauty, or recovery class slots near a location and return matching results as structured JSON (class id, instructor, venue, start/end time in tz, credit cost, premium flag, modality, spots, amenities, rating). Accepts free-form intent, ZIP/city + category + date, a direct /search URL, or a venue slug. Read-only — never books. |
| website | classpass.com |
| category | fitness-wellness |
| tags | - classpass - fitness - yoga - wellness - scheduling - read-only |
| source | 'browserbase: agent-runtime 2026-05-15' |
| updated | '2026-05-15' |
| recommended_method | browser |
| alternative_methods | [] |
| verified | false |
| proxies | false |
ClassPass Find Class
Purpose
Given a free-form intent ("yoga tomorrow morning near 10003"), a {ZIP/city, category, date-range, filters} tuple, a direct https://classpass.com/search/... URL, or a venue slug, return matching ClassPass class slots as structured JSON: class id, title, category, instructor, venue (id, name, address, lat/lon, distance), start/end time in ISO 8601 with tz, duration, credit cost, premium-class flag, modality (in-person / livestream / on-demand), spots remaining, difficulty, description, equipment, amenities, photo URLs, studio rating + review count, canonical class-detail URL — plus a region-wide total so the caller knows the slice is partial.
Read-only. Never click Book / Reserve / Confirm. Even with an authenticated context, treat reservation buttons as off-limits.
When to Use
- "any yoga class tomorrow morning near 10003?"
- "find me a 45-min HIIT class in San Francisco under 6 credits this weekend"
- Enumerate a studio's full upcoming schedule by venue slug:
https://classpass.com/studios/y7-studio-flatiron-new-york - A multi-city comparison agent looking at slot inventory across MSAs.
- Anywhere a caller drops a ClassPass search URL and expects a structured list back.
Workflow
ClassPass is a Next.js + Redux SPA. The search-results SSR HTML returns 200 to a bare/data-center IP (Cloudflare does not challenge), but entities.searchSchedules.data is empty in the SSR store — slot times are fetched client-side via XHR to the internal REST API at https://api.classpass.com. The schedule endpoints require a CP-Authorization header, so the only reliable cookieless path is to drive a real browser, let the XHRs settle, then read the now-populated Redux store from the page.
The non-search REST endpoints under api.classpass.com are fully public (verified GET 200 from a bare AWS IP, no auth, no cookies). Use them as supplements for venue metadata, MSA lookup, and location resolution — they are faster than re-driving the browser.
Step 1 — Session (Verified + proxies recommended but not always required)
SID=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r '.id')
browse cloud fetch works against classpass.com and api.classpass.com with no proxy, but the browser path occasionally trips a Cloudflare challenge from data-center IPs. Default ON for safety; drop --proxies if you're cost-sensitive and the bare session loads cleanly.
Step 2 — Resolve the location
The <city-slug> in /search/{city}/{activity} is cosmetic — ClassPass ignores it for geo-scoping and instead reads either (a) the request IP (default) or (b) URL params ?lat=&lon=. To force a specific location:
# Option A — known place_id (use the location/details endpoint to resolve)
browse cloud fetch "https://api.classpass.com/unisearch/v1/location/details/<google_place_id>"
# returns: { lat, lon, formatted_address, timezone, viewport_*, ... }
# Option B — known MSA → use the table from GET /v1/msas
browse cloud fetch "https://api.classpass.com/v1/msas"
# Notable MSAs: 1=New York Metro (new-york), 2=Los Angeles (los-angeles),
# 3=San Francisco (san-francisco), 4=Chicago, 5=Miami, 8=Washington DC,
# 9=Boston, 11=Seattle, 28=London (UK).
Cache the place_id → {lat, lon, tz} and the MSA table; they're stable across requests.
Step 3 — Build the canonical URL
# Canonical SEO URL for an MSA + activity pair:
browse cloud fetch "https://api.classpass.com/unisearch/v1/search_url?tag_id=<TAG>&msa_id=<MSA>"
# 200: { "url": "/search/new-york-metro/massage" }
# 404: { "data": "No SEO slug found for msa ID 1 and tag ID 1" } # the activity-prefixed fitness tags don't have SEO slugs
# Failing that, hand-build:
URL="https://classpass.com/search/<msa-alias>/<activity-slug>?lat=<LAT>&lon=<LON>"
# Optional filters appended as URL params (see Gotcha §URL-filter-params below)
<activity-slug> examples: yoga, pilates, cycling, hiit, barre, boxing, dance, running, martial-arts, swimming, stretching, massage, facial, cryotherapy, sauna, meditation, acupuncture, nails, lashes, brows, hair, gym-time.
Step 4 — Drive the page, wait for slot hydration, read the store
browse cloud browse --connect "$SID" newpage "$URL"
browse cloud browse --connect "$SID" wait load
browse cloud browse --connect "$SID" wait timeout 4000 # XHR settle — slot list lands ~2–3s after `load`
browse cloud browse --connect "$SID" eval "JSON.parse(document.getElementById('store').textContent)" > store.json
Then parse store.json:
const s = require('./store.json');
const sched = s.entities.searchSchedules.data; // map: "{ids}_{scope}_{date}_{offset}" → result
const venues = s.entities.venueByIdV2?.data || {};
const classes = s.entities.classesByVenue?.data || {}; // map: venueId → catalog[]
const filters = s.filterSets.search.filters;
const region = s.filterSets.search.filters.location.value; // { lat, lon, locationName, timezone, ... }
Each searchSchedules.data[key].schedules[] element carries the slot: start_datetime (ISO 8601), duration_minutes, credits, is_premium, premium_credits, instructor_name, spots_remaining, class_id, venue_id, reservation_id, modality, etc. The region-wide total is at searchSchedules.data[key].total_count (and per-page slice in the array).
Step 5 — Apply filters via URL params + page interactions
URL-level filter params (verified to alter SSR / client-side hydration state on the search page):
| Param | Effect | Values |
|---|---|---|
lat= / lon= | Override IP-based geolocation. Required when calling from a non-target IP. | float / float |
date=YYYY-MM-DD | Single date | ISO date |
time=05:00-08:00 | Time-of-day window. Six canonical windows: 05:00-08:00, 08:00-10:00, 10:00-14:00, 14:00-17:00, 17:00-19:00, 19:00-23:00 | one or comma-sep |
radius= | Distance radius | 0.8 / 1.6 / 8 / 16.1 / 40.2 (km), or omit for "Auto" |
level= | Difficulty | level_all, level_beginner, level_advanced |
amenity= | Amenities | shower, locker, parking |
result_type= | Tab | VENUE (default), MOVEMENT, LIVESTREAM, ON_DEMAND |
vertical= | Top-level vertical | all, fitness, wellness, beauty |
For filters not exposed via URL (credits, duration, instructor, multi-amenity multi-select), drive the filter rail interactively then re-read the store after settle.
Step 6 — Enumerate a specific studio's schedule (shortcut path)
If the caller hands you a venue slug or venue ID, skip the search page entirely — the /studios/{venue-alias} page SSRs both the venue profile AND today's schedule into the Redux store:
browse cloud fetch --allow-redirects "https://classpass.com/studios/<venue-alias>"
# Parse <script id="store" type="application/json">...</script>:
# entities.venueByIdV2.data[alias] → full venue (address, amenities, photos, tz, ratings)
# entities.classesByVenue.data[venue_id] → class catalog (no time slots, just class definitions)
# entities.searchSchedules.data["{venueId}_all_{YYYY-MM-DD}_0"] → today's slots inline
Future days require XHR-driven navigation through the studio page's date picker — that's where you'd switch back to scripted browsing.
Step 7 — Supplement with public REST endpoints
After scripted browsing, hydrate any missing per-venue / per-class metadata via these confirmed-public endpoints (no auth, GET only, ~100 ms each):
| GET endpoint | Returns |
|---|---|
/v2/venues/{id-or-alias} | Full venue: amenities (showers/lockers/mats/towels/parking booleans), address, lat/lon, ratings, tz, MSA id, photos, description, requirements, what_to_bring, cancellation_policy |
/v1/venues/{alias}/classes | Class catalog at venue (definitions, not slots) |
/v1/venues/{alias}/classes/{class_alias} | Single class detail |
/v2/venues/{id}/reviews | Recent venue reviews |
/v2/venues/{id}/similarities | Similar nearby venues |
/v2/venues/{id}/nearby_popular | Nearby popular venues |
/v1/msas | All MSAs (city aliases, default lat/lon, tz, currency) |
/unisearch/v1/search_url?tag_id=X&msa_id=Y | MSA+tag → canonical search URL |
/unisearch/v1/location/details/{google_place_id} | Place → {lat, lon, tz, formatted_address, viewport} |
Step 8 — Release session
browse cloud sessions update "$SID" --status REQUEST_RELEASE
Site-Specific Gotchas
- READ-ONLY. Reservation buttons start a flow that consumes credits — never click
Book,Reserve,Confirm. Stop at the listing-detail view. - The
/search/{city}/{activity}URL is SEO-only — the city slug is ignored for geolocation. Without?lat=&lon=, the page resolves the location to the request-IP's city. A bare-IP fetch from AWS us-west-2 resolved to "Boardman, OR" (lat: 45.84, lon: -119.70) regardless of whether the URL saidnew-york,san-francisco, orchicago. Always append?lat=<LAT>&lon=<LON>to force a location. - Canonical search URL is
/search/{msa-alias}/{activity-slug}where msa-alias is the Metro name (e.g.new-york-metro, notnew-york). Both work, but the metro form is whatunisearch/v1/search_urlemits. Use the metro alias when constructing URLs. - Two activity-tag-ID schemes coexist. Wellness/beauty tags are bare integers (
140Bootcamp,142HIIT,1145Massage,1147Facial,1149Cryotherapy,1153Sauna,1155Meditation,1157Acupuncture,16463Brows,16492Lashes,17590Nails,17592Hair). Fitness tags are prefixed (activity-1Yoga,activity-23Martial arts,activity-72Cycling,activity-90Pilates,activity-100Dance,activity-467Rowing,activity-553Barre,activity-587Boxing,activity-589Running,activity-590Sports,activity-591Outdoors). The unisearchsearch_urlendpoint returns 404 ("No SEO slug found") for theactivity-Nform — for those, hand-build the URL with the slug name directly. - Search-page SSR does not pre-populate
searchSchedules. The Redux store (<script id="store">) on/search/...HTML carries filter enums + MSAs + the resolved location, butentities.searchSchedules.data === {}until the client-side XHR settles. Alwayswait timeout 4000afterwait loadbefore reading. - Studio-page SSR DOES pre-populate
searchSchedulesfor the current day — keyed as"{venueId}_all_{YYYY-MM-DD}_0". This is the fastest path to "today at this studio" with zero browser turns. For future dates, drive the studio page's date picker. - The internal API is REST under
api.classpass.com, not GraphQL. Auth header isCP-Authorization(notAuthorization); internal trace header isx-cpinternalrequestid. The full route table is bundled in the SPA's JS atcdn9.classpass.com/dist/...— grep forunisearchorbff/vto find it. - Schedule POST endpoints are auth-gated.
POST /unisearch/v1/layout/{tab},POST /unisearch/v3/layout/map_items,POST /v3/search/schedulesall return 401/403 without a validCP-Authorizationtoken.GET /v1/classes/{id}/schedulesis 403 cookieless. Don't waste time probing for an unauth bypass — confirmed across multiple probes 2026-05-15. - Public REST endpoints are surprisingly generous.
/v1/msas,/v2/venues/{id-or-alias},/v1/venues/{alias}/classes,/v2/venues/{id}/reviews,/v2/venues/{id}/similarities,/unisearch/v1/location/details/{place_id},/unisearch/v1/search_urlall serve cookieless 200 from arbitrary data-center IPs. Use them aggressively for venue / location / MSA metadata to avoid extra browser turns. - Cloudflare protection is mild on search/studio pages, harder on POST endpoints. Bare
browse cloud fetchgot 200 every time on read-only paths. POST/auth surfaces additionally enforceCP-Authorization. Verified + residential proxies haven't been shown necessary for GETs but use them by default for the browser flow. - Per-user credit pricing requires auth. Without a logged-in session,
credit_costreflects the public displayed value. With cookies, the page shows the user's actual price (member rate / premium-credit surcharge / monthly-cap discount). If the caller hands you authed context, capture bothdisplayed_credit_costanduser_credit_cost. - Premium-class flag (
is_premium: true) doubles or triples credit cost. Always emit bothcredit_costandpremium_credit_costwhen premium. Premium status surfaces in the slot object asis_premiumboolean +premium_creditsnumber. reservation_idvsclass_idvsschedule_id—class_idis the immutable catalog ID (one per "Slow Burn Vinyasa Express" at this studio, ID2220869).schedule_idis per-occurrence.reservation_idonly exists once a slot is held by a specific user. The canonical "this exact slot at this exact time" identifier isschedule_id. Emit all three when surfaced.- Timezones are per-venue, not per-MSA.
venueByIdV2.data[alias].tzis the source of truth. Always renderstart_timein the venue's tz, not the search location's tz (an MSA can span multiple tz, e.g. NY Metro touches Connecticut + NJ; SF Bay touches PT only but London touches BST/GMT). - Modality filter via
result_typeURL param —VENUE(default, in-person),MOVEMENT(search by class type across studios),LIVESTREAM,ON_DEMAND. Emitmodalityon every slot. - Pagination via
getSchedulesByCursor— once a POST/v3/search/scheduleslands, the response includes a cursor forGET /v3/search/schedules?cursor=...to page through results. Browser-driven flow handles this implicitly via infinite scroll; API-replay flow needs explicit cursor handoff. - The 6 canonical time-of-day windows are off-by-one from the prompt's 5. Prompt says
Early morning, Morning, Midday, Afternoon, Evening; ClassPass actually offers05:00-08:00(Early Morning),08:00-10:00(Late Morning),10:00-14:00(Midday),14:00-17:00(Afternoon),17:00-19:00(Evening),19:00-23:00(Late evening). Map the caller's "morning" to both05:00-08:00and08:00-10:00, and "evening" to both17:00-19:00and19:00-23:00. - Live-WebSocket caveat (build context). This skill was developed with
browse cloud fetch-only reconnaissance because the build sandbox could not reachconnect.usw2.browserbase.com(DNS REFUSED). The browser-driving flow in Step 4 is the documented design but was NOT exercised end-to-end during the build. An agent running the skill from a normal Browserbase context (with full WebSocket reach) should expect Step 4 to work as written; if it doesn't, the studio-page SSR shortcut (Step 6) and the public REST endpoints (Step 7) are independently verified fallbacks.
Expected Output
Successful search with slots:
{
"success": true,
"query": {
"location": "New York, NY",
"postal_code": "10003",
"lat": 40.7331,
"lon": -73.9889,
"msa_id": 1,
"msa_alias": "new-york-metro",
"category": "yoga",
"activity_tag_id": "activity-1",
"date": "2026-05-15",
"time_of_day": ["05:00-08:00", "08:00-10:00"],
"modality": "VENUE",
"radius_km": 8,
"level": null,
"amenities": []
},
"region_total": 412,
"returned": 24,
"page": 1,
"next_cursor": "eyJvZmZzZXQiOjI0LCJyZXN1bHRfaWQiOiI2NzQ1NTg0NTUwODA4ODE2OSJ9",
"classes": [
{
"class_id": 240975,
"schedule_id": 86103412,
"reservation_id": null,
"title": "WeFlowHard® Vinyasa",
"class_alias": "weflowhard-vinyasa-yoga-tqna",
"category": "fitness",
"subcategory": "yoga",
"activity_tag_id": "activity-1",
"instructors": ["Jane Doe"],
"venue": {
"venue_id": 27696,
"name": "Y7 Studio",
"subtitle": "Flatiron",
"alias": "y7-studio-flatiron-new-york",
"address": "25 W 23rd St, 3rd floor, New York, NY 10010",
"latitude": 40.7421758,
"longitude": -73.9904711,
"msa_id": 1,
"location_id": 9012,
"neighborhood": "NoMad",
"distance_miles": 0.42
},
"start_time": "2026-05-15T07:30:00-04:00",
"duration_minutes": 60,
"end_time": "2026-05-15T08:30:00-04:00",
"timezone": "America/New_York",
"credit_cost": 8,
"displayed_credit_cost": 8,
"user_credit_cost": null,
"is_premium": false,
"premium_credit_cost": null,
"modality": "in-person",
"spots_remaining": 4,
"capacity": 25,
"difficulty": "level_all",
"description": "Open to all levels, WeFlowHard® Vinyasa is Y7’s signature class…",
"equipment_required": null,
"amenities": {
"showers": true,
"lockers": true,
"mats": true,
"towels": true,
"parking": false
},
"photo_urls": [
"https://classpass-res.cloudinary.com/image/upload/f_auto/q_auto/xbh3bhjd5xpz6mimjbev.jpg"
],
"studio_rating": 4.78,
"studio_review_count": 166681,
"url": "https://classpass.com/classes/weflowhard-vinyasa-yoga-tqna"
}
],
"error_reasoning": null
}
Empty result (location resolved fine, but no slots match filters):
{
"success": true,
"query": { "...": "..." },
"region_total": 0,
"returned": 0,
"classes": [],
"error_reasoning": null
}
Venue-slug enumeration (Step 6 shortcut — venue + today's slots from SSR):
{
"success": true,
"query": { "venue_alias": "y7-studio-flatiron-new-york", "date": "2026-05-15" },
"venue": { "venue_id": 27696, "name": "Y7 Studio", "subtitle": "Flatiron", "...": "..." },
"classes": [ { "...": "..." } ],
"next_dates_require_browser": true,
"error_reasoning": null
}
Failure (location couldn't be resolved):
{
"success": false,
"error_reasoning": "Could not resolve location 'Boardman, OR' to a ClassPass MSA. Falling back to IP geolocation surfaced no slots within radius. Suggest caller supply lat/lon or a known MSA alias.",
"ip_resolved_to": "Boardman, OR",
"classes": []
}
Auth wall (somehow reached an authed endpoint without credentials):
{
"success": false,
"error_reasoning": "POST /unisearch/v1/layout/search returned 401 — endpoint requires CP-Authorization header. Use the SSR-hydrated store path instead, or supply a logged-in session via Browserbase context.",
"classes": []
}
How to use find-class on Cursor
AI-first code editor with Composer
Prerequisites
Before installing skills in Cursor, ensure your development environment meets these requirements:
- ›Cursor installed and configured on your development machine
- ›Node.js version 16.0+ with npm package manager (verify with
node --version) - ›Active project directory or workspace where you want to add find-class
Execute installation command
Execute the skills CLI command in your project's root directory to begin installation:
The skills CLI fetches find-class from GitHub repository classpass.com/find-class-uoiq0m and configures it for Cursor.
Select Cursor when prompted
The CLI will show a list of available agents. Use arrow keys to navigate and space to select Cursor:
Verify installation
Confirm successful installation by checking the skill directory location:
Reload or restart Cursor to activate find-class. Access the skill through slash commands (e.g., /find-class) or your agent's skill management interface.
Security & Verification Notice
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 development environment. Always verify the publisher's identity, review recent commits, and test in isolated environments before production deployment.
List & Monetize Your Skill
Submit your Claude Code skill and start earning
Use Cases▌
Task Automation & Efficiency
Automate repetitive workflows and reduce manual effort
Example
Generate reports, summarize documents, draft communications
Save 3-5 hours per week on routine tasks
Knowledge Enhancement
Learn new skills, understand complex topics, get expert guidance
Example
Explain concepts, provide examples, suggest learning resources
Accelerate learning and skill development by 2x
Quality Improvement
Enhance output quality through reviews, suggestions, and refinements
Example
Review drafts, suggest improvements, catch errors
Improve work quality by 30-40% with less effort
Implementation Guide▌
Prerequisites
- ›Claude Desktop or compatible AI client with skill support
- ›Clear understanding of task or problem to solve
- ›Willingness to iterate and refine outputs
Time Estimate
15-45 minutes depending on use case complexity
Installation Steps
- 1.Install skill using provided installation command
- 2.Test with simple use case relevant to your work
- 3.Evaluate output quality and relevance
- 4.Iterate on prompts to improve results
- 5.Integrate into regular workflow if valuable
Common Pitfalls
- ⚠Expecting perfect results without iteration
- ⚠Not providing enough context in prompts
- ⚠Using skill for tasks outside its intended scope
- ⚠Accepting outputs without review and validation
Best Practices▌
✓ Do
- +Start with clear, specific prompts
- +Provide relevant context and constraints
- +Review and refine all outputs before using
- +Iterate to improve output quality
- +Document successful prompt patterns
✗ Don't
- −Don't use without understanding skill limitations
- −Don't skip validation of outputs
- −Don't share sensitive information in prompts
- −Don't expect skill to replace human judgment
💡 Pro Tips
- ★Be specific about desired format and style
- ★Ask for multiple options to choose from
- ★Request explanations to understand reasoning
- ★Combine AI efficiency with human expertise
When to Use This▌
✓ 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.
Learning Path▌
- 1Familiarize yourself with skill capabilities and limitations
- 2Start with low-risk, non-critical tasks
- 3Progress to more complex and valuable use cases
- 4Build expertise through regular use and experimentation
Discussion
Product Hunt–style comments (not star reviews)- No comments yet — start the thread.
Ratings
4.5★★★★★34 reviews- ★★★★★Amelia Mensah· Dec 24, 2024
find-class has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Ishan Yang· Dec 24, 2024
find-class reduced setup friction for our internal harness; good balance of opinion and flexibility.
- ★★★★★Rahul Santra· Nov 27, 2024
find-class is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
- ★★★★★Kaira Wang· Nov 15, 2024
find-class fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
- ★★★★★Isabella Brown· Nov 15, 2024
I recommend find-class for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
- ★★★★★Amelia Kim· Nov 7, 2024
Keeps context tight: find-class is the kind of skill you can hand to a new teammate without a long onboarding doc.
- ★★★★★Amelia Garcia· Oct 26, 2024
find-class is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
- ★★★★★Pratham Ware· Oct 18, 2024
Keeps context tight: find-class is the kind of skill you can hand to a new teammate without a long onboarding doc.
- ★★★★★Li Perez· Oct 6, 2024
We added find-class from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
- ★★★★★Amelia Flores· Oct 6, 2024
Useful defaults in find-class — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
showing 1-10 of 34