iClassPro has an open, unauthenticated JSON API at app.iclasspro.com/api/open/v1/<slug>/, and it returns live class openings, schedules, tuition and capacity with no login, no key and no scraping. It is undocumented and unsupported, which is why almost everyone concludes iClassPro has no API at all. If you run a gym, swim school, dance studio or cheer program on iClassPro and you need class data on your own website, in a CRM, or in a dashboard, this is the reference nobody else publishes.
Definition: the "open API" is the backing service for iClassPro's parent facing booking portal (portal.iclasspro.com/<slug>/booking). The portal is a single page app, and it has to fetch its catalog from somewhere. That somewhere is a public JSON endpoint, which means anything the portal can show a parent, you can read programmatically.
Every endpoint and field below was verified live on 2026-08-21 against two real accounts.
The problem it solves
The received wisdom is correct as far as it goes. iClassPro publishes no developer documentation, offers no Zapier app, has no webhook system, and has said it has no plans to build a public API. Search the question and you will find vendor pages and third-party writeups all repeating the same conclusion: integrate through the hosted portal, or do not integrate.
So operators do the only thing left. Someone logs into the office portal, runs a report, exports a CSV, and hand-carries the data wherever it needs to go. On the website side it is worse: most iClassPro sites either iframe the portal or retype the class schedule by hand, which means the moment a class fills or a session is cancelled, the public site is lying to customers. We have seen a booking taken for a class that had been deleted hours earlier.
| Task | Portal-only approach | Open API |
|---|---|---|
| Get the class catalog | Log in, run a report, export CSV | One GET, JSON |
| Know remaining spots | Whatever the last export said | openings, live |
| Get tuition and term | Retyped by hand | tuition, tuitionTerm |
| Keep a custom site accurate | Manual updates, always stale | Fetch on page load |
| Verify a spot at checkout | Not possible | One GET before payment |
| Credentials needed | Staff login | None |
The last row is the one that matters most and gets missed. A nightly sync cannot stop a customer buying the last spot twice, because the race happens inside your sync window. A read at the moment of checkout can.
The endpoints
Base URL: https://app.iclasspro.com/api/open/v1/<slug>/
<slug> is the account identifier in the customer portal URL. If your booking portal is portal.iclasspro.com/acmegym/booking, your slug is acmegym.
| Endpoint | Returns |
|---|---|
GET /classes | Class list. Supports limit and page. |
GET /classes/<id> | Single class, with description, tuition and location. |
GET /locations | Locations for the account. |
GET /camps | Camp listings. |
GET /appointments | Appointment-type offerings. |
Endpoints that 404, so do not waste time on them: programs, levels, settings, instructors, enrollment-types, waitlists. Program and level names come back on the class detail record instead.
Slug validation is one call. An unknown slug returns a clean 400:
{"data": false, "status": {"code": 400}, "message": "Organization not found"}Response envelope
{
"totalRecords": 83,
"excludeTotal": false,
"forceStartDate": null,
"showFutureOpenings": true,
"data": [ ... ],
"message": null,
"errors": null
}totalRecords is the full catalog count, which is how you decide whether to page.
Field reference
List record (/classes)
The fields worth building on:
| Field | Notes |
|---|---|
id | Use for the detail call. |
name | Class name as shown to parents. |
openings | Live remaining capacity. The whole reason to use this API. |
futureOpenings, futureOpeningDate | Capacity for an upcoming session. |
schedule[] | Array of {dayNumber, dayName, startTime, endTime, timeStamp, duration}. |
startDate, endDate | Session bounds. |
minAgeYear / minAgeMonth / minAgeDays | Age gates, split across three fields. Same for maxAge*. |
programId, levelId | Grouping keys. Names only appear on the detail record. |
allowWebRegistration | Whether parents may self-register. |
allowWaitlist, autoApprove | Waitlist behaviour. |
availableDates[], availableDays | For drop-in and one-day styles. |
showOpenings | Display flag. Read the gotcha below before trusting it. |
sessions, limitMin, limitMax | Punch-pass and enrollment limits. |
Detail record (/classes/<id>)
Everything above, plus:
| Field | Notes |
|---|---|
tuition | String, e.g. "117.0000". Parse it, do not display it raw. |
tuitionTerm | e.g. "Monthly". |
description | HTML. Sanitize before rendering. |
programName, levelName, roomName | The human labels for the IDs above. |
locationId | Join to /locations. |
regStartDate, regEndDate | Registration window. |
priorityRegStart, priorityRegEnd | Early access window. |
allowTrial, allowWaitlistIfNotFull | Trial and waitlist policy. |
waitlistEnrollments | Current waitlist depth. |
isActive, showOnWeb | Visibility flags. |
image | Class image, if set. |
A real record, trimmed:
{
"id": 225,
"name": "Advanced Tumbling",
"programName": "Tumbling",
"levelName": "Basic Tumbling",
"roomName": "Gymnastics",
"openings": 1,
"showOpenings": false,
"tuition": "117.0000",
"tuitionTerm": "Monthly",
"allowTrial": true,
"isActive": 1,
"schedule": [
{"dayNumber": 5, "dayName": "Thu", "startTime": "5:00PM",
"endTime": "5:55PM", "timeStamp": 61200, "duration": 3300}
]
}Step-by-step: how to use it
Step 1: Confirm your slug
curl -s "https://app.iclasspro.com/api/open/v1/<slug>/classes?limit=1" | head -c 200A 400 Organization not found means the slug is wrong. Try the exact string from your portal URL, and remember it is not always your domain name: hyphenation and abbreviations are common.
Step 2: Pull the whole catalog in one request
curl -s "https://app.iclasspro.com/api/open/v1/<slug>/classes?limit=500"Read totalRecords first. If it exceeds your limit, page with page=2 and so on. Most single-location accounts fit comfortably in one call.
Step 3: Filter locally, not in the query
const res = await fetch(`https://app.iclasspro.com/api/open/v1/${slug}/classes?limit=500`);
const { data, totalRecords } = await res.json();
const bookable = data.filter(
(c) => c.allowWebRegistration && c.openings > 0
);Filter params like programId, dayNumber and hasOpenings do not change totalRecords, so filtering appears to happen client side in the portal. Pull once, filter in your own code.
Step 4: Hydrate the classes you actually display
const detail = await Promise.all(
bookable.slice(0, 40).map((c) =>
fetch(`https://app.iclasspro.com/api/open/v1/${slug}/classes/${c.id}`)
.then((r) => r.json())
.then((j) => (Array.isArray(j.data) ? j.data[0] : j.data))
)
);The list endpoint has no tuition and no program names, so a catalog page needs the detail call. Hydrate only what you render, and cache it.
Step 5: Validate the shape on every pull
const REQUIRED = ["id", "name", "openings", "schedule"];
function assertShape(rows) {
const bad = rows.filter((r) => REQUIRED.some((k) => !(k in r)));
if (bad.length) throw new Error(`iClassPro shape changed: ${bad.length} rows missing fields`);
return rows;
}This is undocumented and unsupported. It can change without notice, and the failure mode you must avoid is silently publishing an empty or wrong catalog. Throw loudly and alert yourself; never fall back to an empty array.
Step 6: Re-check openings at checkout
async function stillAvailable(slug, classId) {
const r = await fetch(`https://app.iclasspro.com/api/open/v1/${slug}/classes/${classId}`);
const j = await r.json();
const c = Array.isArray(j.data) ? j.data[0] : j.data;
return (c?.openings ?? 0) > 0;
}Call this immediately before taking payment. No cache interval, however short, can prevent two parents buying the same last spot. A read at the moment of purchase can.
Step 7: Cache, and be a good citizen
Cache the catalog for a few minutes and serve your pages from the cache. Reserve live calls for the checkout check. This is somebody else's infrastructure serving real parents; polling it aggressively is both rude and the fastest way to get the endpoint locked down for everyone.
Where it gets complicated
showOpenings: false does not hide the number from you. It controls whether the portal displays remaining spots. The API returns openings regardless. The record shown above is a live example: showOpenings is false and openings is 1. If the business has deliberately chosen not to show scarcity to parents, publishing that count on your own site overrides a decision they made on purpose. Read it for logic, think before you render it.
It is read only, and that is the hard boundary. There is no open write endpoint. Creating an enrollment still means driving the authenticated staff portal, which is a genuinely difficult piece of work: family and student matching, makeup tokens, idempotency so a retry does not double-book, and the double-charge risk when your own site has already taken payment through Stripe. Anyone quoting you a two-way sync as if it were the same size job as reading the catalog has not built one.
Tuition is a string with four decimal places. "117.0000" is not a number and is not currency-formatted. Parse it, and decide explicitly what to do about tax and currency. iClassPro's own currency handling has historically been US-centric, which is a live problem for operators outside the US.
Ages arrive in three fields, not one. minAgeYear, minAgeMonth and minAgeDays have to be combined before you can compare against a child's birthday. Treating minAgeYear alone as the age gate will quietly admit children who are months too young.
The class list and the detail record disagree about what exists. Program and level names are on the detail record only, so any UI that groups by program from the list endpoint alone has to make a second round of calls or maintain its own mapping.
Nothing here is a contract. No documentation means no deprecation policy and no support ticket to file when it changes. Mitigate with schema validation and alerting on every pull, not by assuming stability. That is a real operating cost and it belongs in the quote.
What this actually changes
For any read of class data, this removes the entire fragile layer that iClassPro integrations normally rest on. We run a production pipeline for a gym and family entertainment operator that logs into iClassPro with a headless browser, runs a report and exports a CSV on a schedule, described in our iClassPro to GoHighLevel guide. That approach works, and for anything involving enrollments it is still the only way. But for reading the catalog it is the wrong tool: a browser session can break because a button moved, while a JSON endpoint that the vendor's own customer portal depends on is far less likely to change quietly.
The structural gain is real-time accuracy. A custom booking site, a class schedule page, a capacity dashboard or an availability check at checkout all become a fetch instead of a sync. Nothing is stale between runs, because there are no runs.
Frequently asked questions
Does iClassPro have an official API?
No. There is no public, documented, supported API, and iClassPro has said it does not plan to build one. What exists is the open JSON service behind the parent booking portal, which is unauthenticated and returns live class data. It is real and it works, but it is undocumented, so treat it as something that could change without notice.
Do I need an iClassPro plan or API key to use it?
No key, no login and no particular plan. The endpoints serve the public booking portal, so they return the same catalog data any parent could see by browsing your portal. If your portal is public, this data is already public.
Can it create enrollments or write data back?
No. Every open endpoint is read only. Writing an enrollment means automating the authenticated staff portal, which is a substantially harder and riskier build because of student matching, makeup tokens, idempotency and payment reconciliation. Plan reads and writes as two different projects with two different price tags.
Is using an undocumented endpoint safe or allowed?
It serves public, non-personal catalog data to an unauthenticated public portal, so reading it is not a security bypass and exposes no customer information. The practical risks are stability, not legality: it can change without warning, so validate the response shape on every pull and alert on failure. Cache aggressively and keep request volume low.
How do I find my account slug?
It is the path segment in your customer booking portal URL: portal.iclasspro.com/<slug>/booking. Verify it with a single request, because an unknown slug returns a clear 400 Organization not found rather than an empty result.
Can a non-developer set this up?
Reading the catalog is a genuinely small job for anyone comfortable with an API call, and a competent no-code tool can do it. What is not small is everything around it: schema validation, caching, error alerting, age-gate logic, and the checkout-time availability check. Those are what separate a demo from something you can run a business on.
If you are connecting iClassPro to a website, a CRM or a custom booking flow, this is the read layer we build on, and we handle the harder write path separately. See our workflow automation and custom integration work, the full iClassPro to GoHighLevel pipeline, or book a call and we will tell you which half of the problem you actually have.
Want us to build this for you?
15-minute discovery call. No pitch. We tell you what to automate first.
Book a Discovery Call