Skilzy API for training organisations
The Skilzy REST API lets your organisation automatically pull your learners' data (activity, attendance, evaluations, documents, satisfaction) into your own tools: Airtable, spreadsheets, ETL, BI. Mostly read; a write operation also lets you enrol a learner (key with the “write” scope). Scoped to your organisation. These are the same figures as the PDF binders.
Quickstart
Three steps to your first call:
- Generate an API key from the “API & integrations” page in your dashboard (it starts with
sk_live_). - Make your first call, passing the key in the Authorization header:
curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?limit=100"
- You get a JSON response: rows are in
data, with pagination inpagination.{ "resource": "learners", "meta": { "org_name": "Mon OF", "year": 2026, "generated_at": "..." }, "pagination": { "limit": 100, "offset": 0, "count": 42, "total": 42, "has_more": false }, "data": [ { "nom": "Jean Dupont", "email": "jean@…", "temps_actif_min": 184, "evaluations_reussies": 12, … } ] }
Authentication
Every request must carry an organisation API key in the HTTP header. Create and manage your keys from the “API & integrations” page in your dashboard.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
- The key is shown only once at creation; copy it right away.
- A key only grants access to your organisation's data.
- You can revoke a key at any time; it then returns a 401 error.
- A key can have an expiry date (set at creation). After that date it returns 401 “API key expired”; generate a new one.
- Never expose a key in a browser / public front-end.
Endpoints
Base URL: https://skilzy.io/api/v1
All resources are queried with GET:
| Endpoint | Content |
|---|---|
| GET /api/v1/learners | Per-learner summary (1 row / learner) |
| GET /api/v1/users | Org learners + assigned license (1 row / learner) |
| GET /api/v1/summary | Counters: learners + licenses by type (single object in data, not an array) |
| GET /api/v1/attendance | Tutoring attendance (1 row / learner × session) |
| GET /api/v1/evaluations | Quiz attempts (1 row / attempt) |
| GET /api/v1/quiz-answers | Quiz answers question by question (1 row / attempt × question) |
| GET /api/v1/documents | Issued PIF & certificates (1 row / document) |
| GET /api/v1/satisfaction | Satisfaction surveys (1 row / survey) |
| GET /api/v1/enrollments | Progress per learner × programme (1 row / enrolment) |
| GET /api/v1/progress | Detailed progress (1 row / learner × programme × lesson) |
| GET /api/v1/logins | Timestamped logins (1 row / login) |
| GET /api/v1/tutoring-ratings | Tutoring ratings (session + trainer) (1 row / rating) |
| GET /api/v1/lesson-ratings | Milestone / module-end lesson ratings (1 row / rating) |
Query parameters
| Parameter | Description | Default |
|---|---|---|
| format | Output format: json or csv | json |
| year | Filtered calendar year (e.g. 2026) | Current year |
| limit | Rows per page (max 1000, JSON) | 100 |
| offset | Pagination offset (JSON) | 0 |
| updated_since | Only rows whose date ≥ value (YYYY-MM-DD). Incremental sync. Ignored for learners. | — |
| Restricts to ONE learner (exact email, case-insensitive). 404 if unknown in your organisation. | — | |
| user_id | Like email, but by the learner's technical identifier. | — |
| programme | Restricts to ONE programme (programme slug, e.g. marketing-contenus-rs7439). Ignored on resources without a programme (learners, logins). | — |
Single-learner requests (email or user_id) are served through a direct path: they are not affected by the org-wide aggregation cap (meta.truncated) and return a meta.learner block.
# Un seul apprenant (toutes les ressources acceptent ?email= ou ?user_id=) curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?email=jean@orga.fr" # Son avancement sur UNE formation (première/dernière connexion incluses) curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/enrollments?email=jean@orga.fr&programme=marketing-contenus-rs7439" # Ses réponses de quiz, question par question curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/quiz-answers?email=jean@orga.fr"
Response format
JSON responses contain metadata, pagination and rows in data:
{
"resource": "learners",
"meta": { "org_name": "Mon OF", "year": 2026, "generated_at": "..." },
"pagination": { "limit": 100, "offset": 0, "count": 42, "total": 42, "has_more": false },
"data": [
{ "nom": "Jean Dupont", "email": "jean@…", "temps_actif_min": 184, "evaluations_reussies": 12, … }
]
}With ?format=csv, the response is a downloadable CSV file (UTF-8 with BOM, comma-separated), without pagination.
Exception: the summary resource returns a single object in data (not an array) and has no pagination:
{
"resource": "summary",
"meta": { "org_name": "Mon OF", "generated_at": "2026-06-10T09:00:00.000Z" },
"data": {
"utilisateurs": { "total": 38, "actifs": 31 },
"licences": {
"standard": { "total": 50, "disponibles": 12, "utilisees": 38 },
"tutorat": { "total": 10, "disponibles": 4, "utilisees": 6 }
}
}
}Field reference
JSON keys are always in French regardless of language. Boolean values are returned as “Oui” / “Non”.
/api/v1/learners
| Field | Description |
|---|---|
| nom | Learner name |
| invite_le | Invitation date (YYYY-MM-DD) |
| inscrit_le | Enrolment date |
| connexions | Number of logins in the period |
| temps_actif_min | Total active time (minutes, deduplicated) |
| temps_cours_min | Of which time on courses |
| temps_labia_min | Of which AI Lab time |
| temps_tutorat_min | Of which live tutoring time |
| programmes_suivis | Number of programmes followed |
| programmes_completes | Number of programmes 100% complete |
| evaluations_reussies | Passed evaluations |
| evaluations_total | Evaluations attempted (total) |
| score_moyen | Average score of passed evaluations (/100) |
| seances_tutorat | Tutoring sessions attended |
| seances_present | Sessions with validated attendance |
| pif_emis | Number of PIFs issued |
| attestations_emises | Number of certificates issued |
| satisfaction_note | Latest satisfaction rating (/5) |
| nps | Latest NPS (/10) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/learners"
# data[0] :
{
"nom": "Jean Dupont",
"email": "jean@orga.fr",
"invite_le": "2026-01-05",
"inscrit_le": "2026-01-06",
"connexions": 46,
"temps_actif_min": 184,
"temps_cours_min": 150,
"temps_labia_min": 22,
"temps_tutorat_min": 12,
"programmes_suivis": 1,
"programmes_completes": 1,
"evaluations_reussies": 12,
"evaluations_total": 14,
"score_moyen": 87,
"seances_tutorat": 2,
"seances_present": 2,
"pif_emis": 1,
"attestations_emises": 1,
"satisfaction_note": 5,
"nps": 9
}/api/v1/users
| Field | Description |
|---|---|
| nom | Learner name |
| statut | Account status (actif / invité / suspendu) |
| licence_type | License type (standard / tutorat, empty if none) |
| licence_statut | License status (disponible / attribuée / activée / révoquée) |
| inscrit_le | Enrolment or invitation date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/users"
# data[0] :
{
"nom": "Jean Dupont",
"email": "jean@orga.fr",
"statut": "actif",
"licence_type": "standard",
"licence_statut": "activée",
"inscrit_le": "2026-01-06"
}/api/v1/summary
| Field | Description |
|---|---|
| utilisateurs.total | Total number of learners |
| utilisateurs.actifs | Active learners |
| licences.standard.total | Standard licenses (total) |
| licences.standard.disponibles | Available standard licenses |
| licences.standard.utilisees | Used standard licenses |
| licences.tutorat.total | Tutoring licenses (total) |
| licences.tutorat.disponibles | Available tutoring licenses |
| licences.tutorat.utilisees | Used tutoring licenses |
/api/v1/attendance
| Field | Description |
|---|---|
| apprenant | Learner name |
| seance | Session title |
| programme | Linked programme (title) |
| programme_slug | Stable programme identifier (join key) |
| date | Start date & time (YYYY-MM-DD HH:MM) |
| duree_prevue_min | Planned session duration (minutes) |
| present | Attendance validated (Oui / Non) |
| temps_connecte_min | Actual connected time (minutes) |
| signe_apprenant | Signed by learner (Oui / Non) |
| signe_formateur | Signed by trainer (Oui / Non) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/attendance"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"seance": "Tutorat : bien démarrer",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"date": "2026-03-12 14:00",
"duree_prevue_min": 60,
"present": "Oui",
"temps_connecte_min": 58,
"signe_apprenant": "Oui",
"signe_formateur": "Oui"
}/api/v1/evaluations
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Programme (title) |
| programme_slug | Stable programme identifier (join key) |
| module | Module |
| lecon | Evaluated lesson |
| tentative | Attempt number |
| score | Score obtained (/100) |
| reussi | Attempt passed (Oui / Non) |
| date | Attempt date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/evaluations"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"tentative": 2,
"score": 85,
"reussi": "Oui",
"date": "2026-03-15"
}/api/v1/quiz-answers
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Programme (title) |
| programme_slug | Stable programme identifier (join key) |
| module | Module |
| lecon | Lesson containing the quiz |
| tentative | Attempt number |
| score_tentative | Attempt score (/100) |
| reussi | Attempt passed (Oui / Non) |
| question | Question wording |
| options | Offered options, separated by “|” |
| reponse_choisie | Answer chosen by the learner |
| bonne_reponse | Expected correct answer |
| correct | Answer correct (Oui / Non) |
| repondu_le | Answer timestamp (YYYY-MM-DD HH:MM) |
| date | Attempt date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/quiz-answers"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"tentative": 2,
"score_tentative": 85,
"reussi": "Oui",
"question": "Que garantit la méthode RMBC ?",
"options": "Un angle clair | Des vues immédiates | Un script long",
"reponse_choisie": "Un angle clair",
"bonne_reponse": "Un angle clair",
"correct": "Oui",
"repondu_le": "2026-03-15 17:38",
"date": "2026-03-15"
}/api/v1/documents
| Field | Description |
|---|---|
| apprenant | Learner name |
| type | Document type (PIF | Attestation) |
| programme | Related programme (title) |
| programme_slug | Stable programme identifier (join key) |
| emis_le | Issue date (YYYY-MM-DD) |
| heures | Hours (PIF: estimated; certificate: measured) |
| score_completion | Completion score (certificates only) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/documents"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"type": "Attestation",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"emis_le": "2026-04-02",
"heures": 21,
"score_completion": 100
}/api/v1/satisfaction
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Evaluated programme (title) |
| programme_slug | Stable programme identifier (join key) |
| note | Satisfaction rating (/5) |
| nps | Net Promoter Score (/10) |
| verbatim | Free-text learner comment |
| repondu_le | Response date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/satisfaction"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"note": 5,
"nps": 9,
"verbatim": "Formation très concrète, je recommande.",
"repondu_le": "2026-04-02"
}/api/v1/enrollments
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Programme (title) |
| programme_slug | Stable programme identifier (join key) |
| pourcentage | Programme progress (%) |
| lecons_vues | Lessons viewed |
| lecons_total | Total lessons |
| complete | Programme completed (Oui / Non) |
| complete_le | Completion date (= certificate issuance, YYYY-MM-DD) |
| derniere_activite_le | Last activity date on the programme (YYYY-MM-DD) |
| premiere_connexion_le | Learner's first launch of the course, date & time (YYYY-MM-DD HH:MM) |
| derniere_connexion_le | Last connection to the course, date & time (YYYY-MM-DD HH:MM) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/enrollments"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"pourcentage": 100,
"lecons_vues": 48,
"lecons_total": 48,
"complete": "Oui",
"complete_le": "2026-04-02",
"derniere_activite_le": "2026-04-01",
"premiere_connexion_le": "2026-01-08 09:12",
"derniere_connexion_le": "2026-04-01 18:47"
}/api/v1/progress
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Programme (title) |
| programme_slug | Stable programme identifier (join key) |
| pourcentage_programme | Overall programme progress (%) |
| module | Module |
| lecon | Lesson |
| vue | Lesson viewed (Oui / Non) |
| visites | Number of lesson visits |
| temps_min | Active time on the lesson (minutes) |
| derniere_visite_le | Last visit date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/progress"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"pourcentage_programme": 100,
"module": "La méthode RMBC",
"lecon": "Pourquoi RMBC",
"vue": "Oui",
"visites": 3,
"temps_min": 14,
"derniere_visite_le": "2026-03-15"
}/api/v1/logins
| Field | Description |
|---|---|
| apprenant | Learner name |
| debut | Login start (YYYY-MM-DD HH:MM) |
| fin | Session end (YYYY-MM-DD HH:MM) |
| duree_min | Session duration (minutes) |
| ip | Login IP address |
| navigateur | Browser / device (user-agent) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/logins"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"debut": "2026-03-15 17:02",
"fin": "2026-03-15 17:58",
"duree_min": 56,
"ip": "92.184.100.10",
"navigateur": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)…"
}/api/v1/tutoring-ratings
| Field | Description |
|---|---|
| apprenant | Learner name |
| seance | Tutoring session title |
| programme | Linked programme |
| programme_slug | Stable programme identifier (join key) |
| formateur | Rated trainer name |
| note_seance | Session rating (/5, empty for legacy imports) |
| note_formateur | Trainer rating (/5) |
| commentaire | Free-text comment |
| soumis_le | Rating date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/tutoring-ratings"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"seance": "Tutorat : bien démarrer",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"formateur": "Camille",
"note_seance": 5,
"note_formateur": 5,
"commentaire": "Très clair.",
"soumis_le": "2026-03-12"
}/api/v1/lesson-ratings
| Field | Description |
|---|---|
| apprenant | Learner name |
| programme | Programme (title) |
| programme_slug | Stable programme identifier (join key) |
| module | Module |
| lecon | Rated lesson (milestone / module-end) |
| note | Lesson rating (/5) |
| commentaire | Free-text comment |
| soumis_le | Rating date (YYYY-MM-DD) |
See a request & response example
curl -H "Authorization: Bearer sk_live_xxx" "https://skilzy.io/api/v1/lesson-ratings"
# data[0] :
{
"apprenant": "Jean Dupont",
"email": "jean@orga.fr",
"programme": "Marketing de contenus IA : Préparation RS7439",
"programme_slug": "marketing-contenus-rs7439",
"module": "La méthode RMBC",
"lecon": "Bilan du module",
"note": 5,
"commentaire": "Super module.",
"soumis_le": "2026-03-16"
}Activity report PDF
The /api/v1/activity-report endpoint returns a learner's audit binder PDF (the same document as the dashboard: logins, per-lesson time, evaluations, attendance, documents). Binary application/pdf response.
curl -H "Authorization: Bearer sk_live_xxx" \ -o rapport-jean.pdf \ "https://skilzy.io/api/v1/activity-report?email=jean@orga.fr&year=2026" # Optionnel : limiter le rapport à une formation # &programme=marketing-contenus-rs7439
| Parameter | Description |
|---|---|
| email / user_id | Target learner (required, either one) |
| year | Report year (default: current year) |
| programme | Limits the report to one programme (slug, optional) |
Enrol a learner
Beyond reading, the API can enrol a learner into your organisation. This operation requires a key with the “write” scope (tick “Allow writing” when creating the key). It assigns an available license from your organisation.
curl -X POST \
-H "Authorization: Bearer sk_live_write_xxx" \
-H "Content-Type: application/json" \
-d '{"email":"apprenant@orga.fr","programme_slugs":["marketing-contenus-rs7439"]}' \
"https://skilzy.io/api/v1/users"Request body (JSON)
| Field | Description |
|---|---|
| Learner email (required) | |
| programme_slugs | Array of programme slugs to assign (optional; e.g. ["marketing-contenus-rs7439"]) |
Responses
| Code | Meaning |
|---|---|
| 201 | Enrolled (status active if the account already existed, pending otherwise, invitation sent) |
| 403 | Read-only key (write scope required) |
| 409 | No license available, or learner already a member |
| 400 | Missing email, unknown programme, or email outside allowed domain |
Bulk enrolment
Send an array of learners via the users field (max 50 per call). Processing is best-effort: each row succeeds or fails independently (already a member, out of licenses…), and the response returns a summary plus per-row results. Re-running the same batch is safe: already-enrolled learners are skipped without consuming another license.
Option atomic: true → the API pre-counts available licenses and rejects the whole batch (409) if there aren't enough, enrolling no one.
# Inscription en lot (max 50 / appel)
curl -X POST \
-H "Authorization: Bearer sk_live_write_xxx" \
-H "Content-Type: application/json" \
-d '{
"users": [
{ "email": "a@orga.fr", "programme_slugs": ["marketing-contenus-rs7439"] },
{ "email": "b@orga.fr" }
],
"atomic": false
}' \
"https://skilzy.io/api/v1/users"
# Réponse : { "summary": { "requested": 2, "enrolled": 2, "failed": 0 },
# "results": [ { "email": "a@orga.fr", "status": "active", "member_id": "…" }, … ] }Webhooks
Instead of polling the API, you can receive important events in near real time (under a minute) on your server. Configure your endpoints (https URL + event selection) from the “API & integrations” page in your dashboard. A signing secret (whsec_…) is shown once at creation.
Available events
| Type | Trigger |
|---|---|
| quiz.completed | Quiz completed: score, pass status and question-by-question detail (answers) |
| course.started | A learner's first launch of a course (first connection to the course) |
| programme.completed | Programme completed: the end certificate has just been issued |
| learner.enrolled | Learner enrolled in your organisation |
| learner.activated | The learner activated their account (first app login) |
| satisfaction.submitted | Satisfaction survey received (rating, NPS, verbatim) |
Delivery format
Each event is POSTed as JSON to your URL, with three headers: X-Skilzy-Event (type), X-Skilzy-Delivery (stable delivery id, identical on every retry: use it to deduplicate) and X-Skilzy-Signature (HMAC signature).
POST https://ton-serveur.fr/webhooks/skilzy
Content-Type: application/json
X-Skilzy-Event: quiz.completed
X-Skilzy-Delivery: 5f0c… # id stable, identique à chaque retry
X-Skilzy-Signature: t=1751980800,v1=a1b2c3…
{
"id": "5f0c…",
"type": "quiz.completed",
"created_at": "2026-07-09T14:32:11.000Z",
"data": {
"learner": { "user_id": "…", "email": "jean@orga.fr", "name": "Jean Dupont" },
"programme": { "slug": "marketing-contenus-rs7439", "title": "Marketing & contenus…" },
"lesson": { "key": "…", "title": "…", "module_title": "…", "course_id": "…" },
"attempt_number": 1,
"score": 85,
"passed": true,
"completed_at": "2026-07-09T14:32:08.000Z",
"answers": [
{
"question": "Que fait la méthode RMBC ?",
"options": ["…", "…", "…"],
"chosen_label": "…",
"correct_label": "…",
"is_correct": true,
"attempt_no": 1,
"answered_at": "2026-07-09T14:31:52.000Z"
}
]
}
}Verify the signature
The signature covers the timestamp and the raw body: v1 = HMAC-SHA256(secret, "t.body"). Recompute it server-side and compare (constant-time comparison), rejecting stale timestamps.
// Node / Express : vérifier X-Skilzy-Signature
import crypto from 'node:crypto'
function verifySkilzySignature(req, secret) {
const header = req.get('X-Skilzy-Signature') || '' // t=1751980800,v1=abc…
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
if (!t || !v1) return false
// Anti-rejeu : refuse les livraisons de plus de 5 minutes.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = crypto
.createHmac('sha256', secret) // secret whsec_…
.update(`${t}.${req.rawBody}`) // corps BRUT, pas re-sérialisé
.digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}Expected responses & retries
- Respond with a 2xx code within 10 seconds (process asynchronously if needed).
- On failure (other code, timeout), delivery is retried: 1 min, 10 min, 1 h, 6 h then 24 h. After 6 attempts it is abandoned (“dead” status in the log).
- 50 consecutive failures automatically disable the endpoint (re-enable it from the dashboard).
- Delivery order is not guaranteed: use the payload's created_at to order events.
- The dashboard's “Test” button sends a signed ping event, visible in the delivery log.
Examples & integrations
curl -H "Authorization: Bearer sk_live_xxx" \ "https://skilzy.io/api/v1/learners?limit=100"
Error codes
| Code | Meaning |
|---|---|
| 401 | Missing, invalid, revoked or expired API key |
| 404 | Unknown resource (check the URL) |
| 405 | Method not allowed (use GET) |
| 429 | Too many requests; honour the Retry-After header |
| 500 | Internal error; retry or contact support |
Limits & guarantees
- Limited writes: keys are read-only by default; only learner enrolment (POST /users) is possible, and only with a “write”-scoped key.
- Scope: a key only sees and acts on its own organisation's learners.
- Volume: up to 50 learners per (bulk) enrolment call; paginate your calls beyond that.
- Rate: 120 requests per minute per key. Beyond that, a 429 response with a Retry-After header.
- Consistency: figures exactly match the PDF binders.
OpenAPI & Postman
Import the API into your client (Postman, Insomnia, Bruno…) or generate a client from the OpenAPI 3.1 spec:
In Postman: Import → Link, and paste the URL https://skilzy.io/openapi.json (or the file). Then set the collection's apiKey variable to your sk_live_… key.
