Daftar isi
- Bentuk response
- Parameter filter wilayah
- Rate limiting
- Caching
- Ringkasan endpoint
- 1. Daftar provinsi
- 2. Daftar kabupaten/kota
- 3. Daftar kecamatan
- Cascading select
- 4. Daftar sekolah
- 5. Detail sekolah
- Error (HTTP 404)
- 6. Daftar perguruan tinggi
- 7. Detail perguruan tinggi
- Error (HTTP 404)
- 8. Daftar program studi
- 9. Status scraping
- Menjalankan scraper
- Struktur database
- Kolom sekolahs
- Kolom sekolah_details (relasi detail)
- Kolom perguruan_tinggis
- Kode status HTTP
- Contoh integrasi
- Deploy
SchoolGateway API — Dokumentasi
Base URL: http://<host>:9004/api/v1
Semua endpoint public (tanpa autentikasi) dan mengembalikan Content-Type: application/json.
Bentuk response
Endpoint daftar (list) selalu:
{
"success": true,
"meta": {
"current_page": 1,
"per_page": 50,
"total": 120,
"last_page": 3
},
"data": [ ... ]
}
Endpoint detail selalu:
{ "success": true, "data": { ... } }
Error selalu:
{ "success": false, "message": "..." }
Semua list endpoint memakai paginasi: per_page default 50, maks 100; page default 1. Angka di meta.total adalah jumlah baris yang cocok dengan filter, bukan jumlah baris di data.
Parameter filter wilayah
provinsi, kabupaten, dan kecamatan pada semua endpoint di bawah dicocokkan terhadap nama, bukan kode.
| Salah | Benar |
|---|---|
?kabupaten=3171 |
?kabupaten=Jakarta |
?provinsi=010000 |
?provinsi=Jakarta |
Untuk pencocokan berdasarkan kode, lihat kolom *_kode yang dikembalikan tiap baris.
Rate limiting
60 request per menit per IP. Melebihi batas menghasilkan HTTP 429.
Caching
Hasil di-cache di Redis, TTL per endpoint:
| Endpoint | TTL |
|---|---|
/provinsi |
24 jam |
/kabupaten, /kecamatan |
12 jam |
/sekolah |
1 jam |
/sekolah/{npsn} |
6 jam |
/perguruan-tinggi |
24 jam |
/perguruan-tinggi/{kode} |
6 jam |
/program-studi |
24 jam |
/scrape/status |
1 menit |
Hasil kosong (list) dan 404 (detail) tidak pernah di-cache, sehingga record yang baru discrape langsung terlihat tanpa menunggu TTL.
Ringkasan endpoint
| Method | Path | Deskripsi |
|---|---|---|
| GET | /api/v1/provinsi |
Daftar provinsi |
| GET | /api/v1/kabupaten |
Daftar kabupaten/kota |
| GET | /api/v1/kecamatan |
Daftar kecamatan |
| GET | /api/v1/sekolah |
Cari / filter daftar sekolah |
| GET | /api/v1/sekolah/{npsn} |
Detail satu sekolah |
| GET | /api/v1/perguruan-tinggi |
Cari / filter perguruan tinggi (PDDikti) |
| GET | /api/v1/perguruan-tinggi/{kode} |
Detail satu perguruan tinggi |
| GET | /api/v1/program-studi |
Cari / filter program studi |
| GET | /api/v1/scrape/status |
Jumlah record & waktu update terakhir per tabel |
1. Daftar provinsi
GET /api/v1/provinsi
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
q |
string | jawa |
Pencarian nama (LIKE) |
jenjang |
string | dikmen |
Hanya provinsi yang punya data jenjang ini |
per_page |
integer | 20 |
Default 50, maks 100 |
page |
integer | 2 |
Default 1 |
curl "http://localhost:9004/api/v1/provinsi"
curl "http://localhost:9004/api/v1/provinsi?q=jawa"
{
"success": true,
"meta": {"current_page": 1, "per_page": 3, "total": 39, "last_page": 13},
"data": [
{"kode": "060000", "nama": "Aceh"},
{"kode": "220000", "nama": "Bali"},
{"kode": "280000", "nama": "Banten"}
]
}
2. Daftar kabupaten/kota
GET /api/v1/kabupaten
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
provinsi |
string | Jakarta |
Nama provinsi induk (LIKE) |
q |
string | bandung |
Pencarian nama (LIKE) |
jenjang |
string | dikmen |
Filter jenjang |
per_page, page |
integer | Seperti di atas |
curl "http://localhost:9004/api/v1/kabupaten?provinsi=Jakarta"
{
"success": true,
"meta": {"current_page": 1, "per_page": 50, "total": 6, "last_page": 1},
"data": [
{"kode": "010100", "nama": "Kab. Adm. Kep. Seribu"},
{"kode": "016200", "nama": "Kota Adm. Jakarta Barat"}
]
}
3. Daftar kecamatan
GET /api/v1/kecamatan
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
kabupaten |
string | Jakarta |
Nama kabupaten induk (LIKE) |
q |
string | menteng |
Pencarian nama (LIKE) |
jenjang |
string | dikmen |
Filter jenjang |
per_page, page |
integer | Seperti di atas |
curl "http://localhost:9004/api/v1/kecamatan?kabupaten=Jakarta"
{
"success": true,
"meta": {"current_page": 1, "per_page": 50, "total": 22, "last_page": 1},
"data": [
{"kode": "016005", "nama": "Cempaka Putih"},
{"kode": "016207", "nama": "Cengkareng"}
]
}
Cascading select
/api/v1/provinsi -> pilih provinsi (kode/nama)
/api/v1/kabupaten?provinsi=Jakarta -> pilih kabupaten
/api/v1/kecamatan?kabupaten=Jakarta -> pilih kecamatan
/api/v1/sekolah?kabupaten=Jakarta -> hasil
4. Daftar sekolah
GET /api/v1/sekolah
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
q |
string | SMAN |
Cari nama atau npsn (LIKE), diurutkan relevansi: persis > diawali > mengandung |
nama |
string | SMAN |
Nama (LIKE) — alias q, tetap didukung |
npsn |
string | 2010 |
NPSN (LIKE) |
provinsi |
string | Jakarta |
Nama provinsi (LIKE) |
kabupaten |
string | Jakarta |
Nama kabupaten (LIKE) |
kecamatan |
string | Senen |
Nama kecamatan (LIKE) |
jenjang |
string | dikmen |
DIKDAS / DIKMEN (otomatis di-uppercase) |
status |
string | NEGERI |
NEGERI / SWASTA (otomatis di-uppercase) |
per_page, page |
integer | Seperti di atas |
curl "http://localhost:9004/api/v1/sekolah?jenjang=dikmen&provinsi=Jakarta"
curl "http://localhost:9004/api/v1/sekolah?nama=SMAN&status=NEGERI&per_page=20"
curl "http://localhost:9004/api/v1/sekolah?q=SMAN%203"
{
"success": true,
"meta": {"current_page": 1, "per_page": 3, "total": 52, "last_page": 18},
"data": [
{
"npsn": "60725437",
"nama": "MAS AL QALAM",
"alamat": "...",
"kelurahan": "...",
"status": "SWASTA",
"jenjang_pendidikan": "DIKMEN",
"kecamatan": "Senen",
"kabupaten_kota": "Kota Adm. Jakarta Pusat",
"provinsi": "D.K.I. Jakarta"
}
]
}
Response list hanya memuat kolom terpilih di atas; kolom *_kode dan created_at/updated_at hanya ada di endpoint detail.
5. Detail sekolah
GET /api/v1/sekolah/{npsn}
curl "http://localhost:9004/api/v1/sekolah/20107185"
{
"success": true,
"data": {
"npsn": "20107185",
"nama": "SMAN 69 JAKARTA",
"alamat": "PULAU PRAMUKA RT 003/05",
"kelurahan": "PULAU PANGGANG",
"kecamatan": "KEPULAUAN SERIBU UTARA",
"kabupaten_kota": "ADM. KEP. SERIBU",
"provinsi": "D.K.I. JAKARTA",
"kecamatan_kode": "010102",
"kabupaten_kode": "010100",
"provinsi_kode": "010000",
"status": "NEGERI",
"jenjang_pendidikan": "DIKMEN",
"detail": {
"akreditasi": "A",
"kementerian_pembina": "Kementerian Pendidikan Dasar dan Menengah",
"naungan": "Pemerintah Daerah",
"luas_tanah": "5445.00",
"sumber_listrik": "PLN",
"akses_internet": null,
"telepon": "02170611769",
"fax": "02146214455",
"email": "ka_sman_69@yahoo.com",
"website": "http://sman69jkt.sch.id",
"operator": "SITI MUKHLISHOH",
"lintang": "-5.74763200",
"bujur": "106.61348640"
}
}
}
detail bisa null bila tahap scraping detail belum berjalan untuk NPSN tersebut.
Error (HTTP 404)
{ "success": false, "message": "Sekolah tidak ditemukan" }
6. Daftar perguruan tinggi
GET /api/v1/perguruan-tinggi
Sumber: PDDikti.
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
nama |
string | akademi |
Nama (LIKE) |
jenis |
string | Swasta |
Swasta / Negeri (case-sensitive sesuai data) |
status |
string | Aktif |
Aktif, Tutup, Alih Bentuk, Alih Kelola, Pembinaan |
akreditasi |
string | Baik |
Nilai akreditasi |
provinsi |
string | ACEH |
Nama provinsi (LIKE) |
kabupaten |
string | Yogyakarta |
Nama kabupaten/kota (LIKE) |
per_page, page |
integer | Seperti di atas |
Perhatian: bila status tidak dikirim (atau dikirim kosong), endpoint ini otomatis membatasi hasil ke status_pt = "Aktif" sehingga meta.total hanya menghitung PT aktif. Kirim ?status= dengan nilai yang valid (Tutup, Alih Bentuk, Alih Kelola, Pembinaan, Aktif) untuk melihat status lain.
curl "http://localhost:9004/api/v1/perguruan-tinggi?per_page=3"
curl "http://localhost:9004/api/v1/perguruan-tinggi?status=Tutup"
{
"success": true,
"meta": {"current_page": 1, "per_page": 3, "total": 62, "last_page": 21},
"data": [
{
"id": 76,
"kode_pt": null,
"nama_normalized": "AKADEMI FARMASI PUTRA INDONESIA MALANG",
"nama": "Akademi Farmasi Putra Indonesia Malang",
"nama_singkat": null,
"jenis_pt": "Swasta",
"status_pt": "Aktif",
"akreditasi": null,
"provinsi": "JAWA TIMUR",
"kabupaten_kota": "Kota Malang",
"jumlah_prodi": 0,
"biaya_min": null,
"biaya_max": null,
"alamat": null,
"kelompok": null,
"pembina": null,
"created_at": "2026-10-06T15:24:38.000000Z",
"updated_at": "2026-10-06T15:24:38.000000Z"
}
]
}
kode_pt hanya terisi untuk PT yang sudah melewati tahap scraping detail. Sisanya null.
7. Detail perguruan tinggi
GET /api/v1/perguruan-tinggi/{kode}
{kode} diterima dalam dua bentuk:
- Kode PT — mis.
053019. - Fallback
nama_normalized— nama huruf besar persis seperti disimpan scraper, mis.SEKOLAH%20TINGGI%20ILMU%20ADMINISTRASI%20AAN. Dipakai untuk PT yangkode_pt-nya masihnull.
| Param | Tipe | Keterangan |
|---|---|---|
with_prodi |
boolean | Hanya kembalikan PT yang punya minimal satu program studi |
curl "http://localhost:9004/api/v1/perguruan-tinggi/053019"
curl "http://localhost:9004/api/v1/perguruan-tinggi/053019?with_prodi=1"
{
"success": true,
"data": {
"id": 4,
"kode_pt": "053019",
"nama_normalized": "SEKOLAH TINGGI ILMU ADMINISTRASI AAN",
"nama": "Sekolah Tinggi Ilmu Administrasi Aan",
"nama_singkat": "STIA \"AAN\"",
"jenis_pt": "Swasta",
"status_pt": "Aktif",
"akreditasi": "Baik Sekali",
"provinsi": "D.I. YOGYAKARTA",
"kabupaten_kota": "Kota Yogyakarta",
"jumlah_prodi": 4,
"biaya_min": 980000,
"biaya_max": 16025000,
"alamat": "Blunyahrejo Karangwaru Tegalrejo",
"kode_pos": "55241",
"kecamatan": "Kec. Tegalrejo",
"email": "stiaaan79@gmail.com",
"telepon": "0274-517471",
"fax": "0274-517471",
"website": "www.stia-aan.ac.id",
"lintang": "-7.77032700",
"bujur": "110.36616100",
"kelompok": "Perguruan Tinggi Swasta",
"pembina": "LLDIKTI V",
"sk_pendirian_sp": "629/KPT/I/2018",
"tgl_berdiri_pt": "1979-06-07T00:00:00.000000Z",
"program_studi": []
}
}
program_studi berisi daftar prodi PT tersebut. Kosong bila tahap scraping prodi belum dijalankan.
Error (HTTP 404)
{ "success": false, "message": "Perguruan tinggi tidak ditemukan" }
8. Daftar program studi
GET /api/v1/program-studi
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
nama |
string | teknik |
Nama prodi (LIKE) |
kode_pt |
string | 053019 |
Prodi milik PT tersebut |
jenjang |
string | S1 |
Jenjang prodi (otomatis di-uppercase) |
akreditasi |
string | Baik |
Akreditasi prodi |
status |
string | Aktif |
Status prodi (otomatis di-uppercase) |
provinsi |
string | Yogyakarta |
Provinsi PT induk (LIKE) |
per_page, page |
integer | Seperti di atas |
curl "http://localhost:9004/api/v1/program-studi?kode_pt=053019"
{
"success": true,
"meta": {"current_page": 1, "per_page": 50, "total": 0, "last_page": 1},
"data": []
}
Setiap baris menyertakan relasi perguruan_tinggi (id, kode_pt, nama).
9. Status scraping
GET /api/v1/scrape/status
| Param | Tipe | Contoh | Keterangan |
|---|---|---|---|
jenjang |
string | dikmen |
Hanya menyaring tabel provinsi, kabupaten, kecamatan, sekolahs. Tabel PDDikti tidak punya kolom jenjang. |
curl "http://localhost:9004/api/v1/scrape/status"
curl "http://localhost:9004/api/v1/scrape/status?jenjang=dikmen"
{
"success": true,
"meta": {"jenjang": "DIKMEN", "generated_at": "2026-10-06T09:07:09+00:00"},
"data": {
"provinsi": {"total": 39, "updated_at": "2026-10-06T15:35:19+00:00"},
"kabupaten": {"total": 526, "updated_at": "2026-10-06T15:35:52+00:00"},
"kecamatan": {"total": 24, "updated_at": "2026-10-06T15:35:56+00:00"},
"sekolah": {"total": 52, "updated_at": "2026-10-06T15:35:59+00:00"},
"sekolah_detail": {"total": 12, "updated_at": "2026-10-06T15:36:09+00:00"},
"perguruan_tinggi": {"total": 101, "updated_at": "2026-10-06T15:34:37+00:00"},
"program_studi": {"total": 0, "updated_at": null}
}
}
updated_at adalah MAX(updated_at) kolom pada tabel tersebut — null berarti tabel masih kosong.
Menjalankan scraper
# Sekolah (perlu --profile scraper; resume otomatis, lanjut dari progres terakhir)
docker compose --profile scraper run --rm scraper --jenjang dikmen
docker compose --profile scraper run --rm scraper --jenjang dikdas
# Periksa proxy sebelum mulai
docker compose --profile scraper run --rm scraper --proxy-check
# Perguruan tinggi + program studi
docker compose --profile scraper run --rm scraper run_pt.py --limit 100 --detail
Struktur database
| Table | Deskripsi |
|---|---|
provinsi |
Provinsi (kode, nama, jenjang) |
kabupaten |
Kabupaten/kota + FK provinsi_kode |
kecamatan |
Kecamatan + FK kabupaten_kode, provinsi_kode |
sekolahs |
Sekolah utama (NPSN, nama, alamat, FK kode wilayah) |
sekolah_details |
Detail sekolah (akreditasi, kontak, koordinat) |
perguruan_tinggis |
Perguruan tinggi dari PDDikti |
program_studis |
Program studi, FK perguruan_tinggi_id |
Kolom sekolahs
| Field | Jenis | Keterangan |
|---|---|---|
npsn |
string | Primary key bisnis (8 digit) |
nama |
string | Nama sekolah |
alamat |
text | Alamat lengkap |
kelurahan |
string | Kelurahan / desa |
kecamatan |
string | Nama kecamatan |
kabupaten_kota |
string | Nama kabupaten/kota |
provinsi |
string | Nama provinsi |
kecamatan_kode |
string | Kode BPS kecamatan |
kabupaten_kode |
string | Kode BPS kabupaten/kota |
provinsi_kode |
string | Kode BPS provinsi |
status |
string | NEGERI / SWASTA |
jenjang_pendidikan |
string | DIKDAS / DIKMEN |
Kolom sekolah_details (relasi detail)
| Field | Jenis | Keterangan |
|---|---|---|
akreditasi |
string | A/B/C/- |
kementerian_pembina |
string | Kementerian pembina |
naungan |
string | Lembaga naungan |
luas_tanah |
string | Luas tanah (m²) |
sumber_listrik |
string | Sumber listrik |
akses_internet |
string | Jenis akses internet |
telepon, fax, email, website |
string | Kontak |
operator |
string | Nama operator data |
lintang, bujur |
string | Koordinat |
Kolom perguruan_tinggis
| Field | Jenis | Keterangan |
|---|---|---|
kode_pt |
string | Kode PDDikti — null bila tahap detail belum jalan |
nama_normalized |
string | Nama huruf besar, dipakai sebagai fallback {kode} |
nama, nama_singkat |
string | Nama lengkap dan singkat |
jenis_pt |
string | Swasta / Negeri |
status_pt |
string | Aktif, Tutup, Alih Bentuk, Alih Kelola, Pembinaan |
akreditasi |
string | Akreditasi PT |
provinsi, kabupaten_kota, kecamatan, alamat, kode_pos |
string | Wilayah dan alamat |
jumlah_prodi |
int | Jumlah program studi |
biaya_min, biaya_max |
int | Rentang biaya (Rp) |
kelompok, pembina |
string | Kelompok PT dan LLDIKTI |
sk_pendirian_sp, tgl_berdiri_pt |
string | Dasar pendirian |
lintang, bujur |
string | Koordinat |
Kode status HTTP
| Status | Kasus |
|---|---|
200 |
Sukses (daftar / detail) |
404 |
{npsn} atau {kode} tidak ditemukan |
429 |
Rate limit terlampaui (60 req/menit per IP) |
500 |
Internal server error |
Contoh integrasi
$base = 'http://localhost:9004/api/v1';
// 1. Pilih provinsi
$provinsi = Http::get($base.'/provinsi')->json('data');
// 2. Kabupaten pada provinsi tersebut
$kabupaten = Http::get($base.'/kabupaten', ['provinsi' => 'Jakarta'])->json('data');
// 3. Sekolah, gabungkan semua filter
$sekolah = Http::get($base.'/sekolah', [
'jenjang' => 'dikmen',
'kabupaten' => 'Jakarta',
'per_page' => 100,
])->json();
foreach ($sekolah['data'] as $s) {
// $s['npsn'], $s['nama'], $s['alamat'], ...
}
// 4. Detail sekolah lengkap (termasuk koordinat)
$detail = Http::get($base.'/sekolah/'.$s['npsn'])->json('data');
// 5. Perguruan tinggi
$pt = Http::get($base.'/perguruan-tinggi', [
'status' => 'Aktif',
'nama' => 'akademi',
])->json();
Deploy
# Jalankan semua container
docker compose up -d
# Scraper sekolah (resume otomatis)
docker compose --profile scraper run --rm scraper --jenjang dikmen
# Scraper perguruan tinggi
docker compose --profile scraper run --rm scraper run_pt.py --limit 100 --detail
Nginx melayani port 9004.