Rex Automaton
All posts
AutomationAugust 21, 202611 min read

Does iClassPro Have an API? Yes, an Undocumented One (Endpoint Reference)

iClassPro says it has no API. Its own parent booking portal runs on one: an open, unauthenticated JSON endpoint that returns live class openings, schedules and tuition. Full endpoint and field reference, verified live, plus the gotchas.

By Jacky Lei

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.

TaskPortal-only approachOpen API
Get the class catalogLog in, run a report, export CSVOne GET, JSON
Know remaining spotsWhatever the last export saidopenings, live
Get tuition and termRetyped by handtuition, tuitionTerm
Keep a custom site accurateManual updates, always staleFetch on page load
Verify a spot at checkoutNot possibleOne GET before payment
Credentials neededStaff loginNone

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.

EndpointReturns
GET /classesClass list. Supports limit and page.
GET /classes/<id>Single class, with description, tuition and location.
GET /locationsLocations for the account.
GET /campsCamp listings.
GET /appointmentsAppointment-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:

FieldNotes
idUse for the detail call.
nameClass name as shown to parents.
openingsLive remaining capacity. The whole reason to use this API.
futureOpenings, futureOpeningDateCapacity for an upcoming session.
schedule[]Array of {dayNumber, dayName, startTime, endTime, timeStamp, duration}.
startDate, endDateSession bounds.
minAgeYear / minAgeMonth / minAgeDaysAge gates, split across three fields. Same for maxAge*.
programId, levelIdGrouping keys. Names only appear on the detail record.
allowWebRegistrationWhether parents may self-register.
allowWaitlist, autoApproveWaitlist behaviour.
availableDates[], availableDaysFor drop-in and one-day styles.
showOpeningsDisplay flag. Read the gotcha below before trusting it.
sessions, limitMin, limitMaxPunch-pass and enrollment limits.

Detail record (/classes/<id>)

Everything above, plus:

FieldNotes
tuitionString, e.g. "117.0000". Parse it, do not display it raw.
tuitionTerme.g. "Monthly".
descriptionHTML. Sanitize before rendering.
programName, levelName, roomNameThe human labels for the IDs above.
locationIdJoin to /locations.
regStartDate, regEndDateRegistration window.
priorityRegStart, priorityRegEndEarly access window.
allowTrial, allowWaitlistIfNotFullTrial and waitlist policy.
waitlistEnrollmentsCurrent waitlist depth.
isActive, showOnWebVisibility flags.
imageClass 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 200

A 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

Related reading