School360 API
Build on School360
A REST API for each school's own systems: biometric attendance, bank and wallet payment confirmations, SMS gateways, accounting and parent apps. JSON in and out, keys the school controls, webhooks when things happen.
curl https://api.school360.cloud/v1/students?limit=2 \
-H "Authorization: Bearer s360_live_…"Get a key
API keys are part of the Enterprise package. The school's owner or admin creates them in School Setup › Integrations › API keys: a name, what the key may read or write (students, families, attendance, fees…), and optionally one campus, the IP addresses it may be used from and an expiry date. The full key is shown once; School360 keeps only a hash. Give each system its own key so you can revoke it on its own.
Keys look like s360_live_… . Test keys (s360_test_…) only work on the Demo School.
Your first request
Every request sends the key in the Authorization header. Ask for the school the key belongs to: if you get its name back, you're connected.
curl https://api.school360.cloud/v1/school \
-H "Authorization: Bearer $SCHOOL360_KEY"// Node.js 18+ (fetch is built in)
const res = await fetch("https://api.school360.cloud/v1/school", {
headers: { Authorization: `Bearer ${process.env.SCHOOL360_KEY}` },
})
const body = await res.json()
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`)
console.log(body.name, body.timezone)<?php
$ch = curl_init('https://api.school360.cloud/v1/school');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SCHOOL360_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException($body['error']['code'] . ': ' . $body['error']['message']);
}
echo $body['name'];import os
import requests # pip install requests
res = requests.get(
"https://api.school360.cloud/v1/school",
headers={"Authorization": f"Bearer {os.environ['SCHOOL360_KEY']}"},
timeout=30,
)
res.raise_for_status()
school = res.json()
print(school["name"], school["timezone"])Keep the key out of your code: read it from an environment variable or your platform's secret store, as the samples do with SCHOOL360_KEY.
Requests and answers
The base address is https://api.school360.cloud/v1. Everything is JSON (send Content-Type: application/json with a body), field names are camelCase, dates are ISO 8601 (YYYY-MM-DD, or a date-time with its offset), and amounts are numbers in the school's currency (PKR). Every answer carries an X-Request-Id: quote it when asking for help.
A student looks like this:
{
"id": 412,
"admissionNo": "ADM-2026-00412",
"firstName": "Ayesha",
"lastName": "Siddiqui",
"classId": 7,
"sectionId": 19,
"status": "active",
"admissionDate": "2026-04-01",
"cardNo": "0004417365",
"updatedAt": "2026-10-08T07:41:12.000Z"
}Permissions and campuses
A key reads or writes only the resources it was given. The school's structure (school, campuses, sessions, classes, sections, subjects) is readable by every key. A key limited to one campus only sees and changes that campus's records. Parents' phone numbers, emails and CNICs need families:read; staff pay, bank accounts and CNICs need salaries:read on top of staff:read (it is never part of a "read everything" key).
- students:read · write
- families:read · write
- attendance:read · write
- fees:read · write
- exams:read · write
- staff:read
- salaries:read
- admissions:read · write
- gate:read
- notices:read · write
Exam results
GET /results?examId=§ionId= (or classId=) answers only once the school has published the exam's results; before that it is 404 results_not_published (the exam.results_published webhook tells you when). Each student has their marks per paper (absent: true when marked absent), total, percentage, grade and remark from the school's grading scheme, pass or fail by the school's rule, and their position in the whole class (null when the school doesn't show positions, or a mark is missing). These are the same figures the report cards print. GET /students/{id}/results gives one student's published results for a session.
curl "https://api.school360.cloud/v1/results?examId=31§ionId=19" -H "Authorization: Bearer $SCHOOL360_KEY"
# → { "exam": { "id": 31, "status": "published", … }, "grading": { "passPercent": 33, "rule": "every-subject", "positions": true },
# "papers": [{ "id": 210, "subjectId": 4, "maxMarks": 100, "passMarks": 33, … }, …],
# "data": [{ "studentId": 412, "total": 512, "max": 600, "percentage": 85.33, "grade": "A", "passed": true, "position": 3, … }] }Where next
Read how lists are paged, what errors look like and how to retry safely, then pick a recipe close to what you're building.
- AuthenticationAPI keys, the Authorization header, test keys, IP allow-lists, expiry and rotation.
- ErrorsStatus codes, the error body and every error code the API can answer with.
- PaginationCursor paging with limit and cursor, and updatedSince for what changed.
- Rate limitsHow many requests a key may make, the X-RateLimit headers and backing off.
- IdempotencySend a POST again safely with an Idempotency-Key, so a payment is never recorded twice.
- Polling for changesGET /changes: a cheap feed of what was added, changed or deleted, for tools without webhooks.
- WebhooksSchool360 calls your system when something happens in a school: admissions, absences, late arrivals, registers, fee payments, vouchers, results, notices, staff, leave and the gate. Signed, retried and logged.
- RecipesWorked examples: biometric machines, SMS on absence, bank payments, accounting exports and Google Sheets.
Postman collection
Every endpoint as a ready-made Postman collection (v2.1), made from the same OpenAPI spec as the reference. Import it, then set two variables: baseUrl (already https://api.school360.cloud/v1) and apiKey (your key). POST requests send a fresh Idempotency-Key each time.