
Kamu lagi membangun form pengajuan cuti di aplikasi HR, dan ada satu pertanyaan yang bikin perhitungannya meleset: kalau karyawan ambil cuti 1 sampai 3 Januari, berapa hari yang sebetulnya memotong saldo cutinya? 1 Januari itu Tahun Baru, harusnya tidak ikut dihitung. API hari libur nasional dari api.co.id menjawab ini lewat satu api call ke GET https://use.api.co.id/holidays/indonesia/check/range dengan header x-api-co-id, dan gratis. Kamu tinggal mengirim rentang tanggal cuti yang diajukan, sistem akan mengembalikan setiap libur dalam rentang itu beserta type-nya, lalu kamu menandai tanggal ber-type Public Holiday atau Joint Holiday (plus akhir pekan) sebagai hari yang tidak memotong saldo. Satu hal yang perlu kamu tahu dari awal: jangan memutuskannya lewat field is_holiday, karena field itu juga bernilai true untuk hari peringatan yang sebenarnya tetap hari kerja.
Yang akan kamu bangun dengan API hari libur nasional: validasi cuti
Hasil akhir yang kita tuju sederhana: form cuti yang menampilkan “cuti ini memotong N hari saldo”, dengan N yang sudah mengecualikan libur nasional, cuti bersama, dan akhir pekan, semuanya dari satu API gratis. Semua panggilan memakai base URL https://use.api.co.id/holidays/indonesia dan header x-api-co-id berisi API key kamu. Tiap panggilan membalas dalam bentuk yang sama, jadi kode pembaca respons yang kamu tulis sekali bisa kamu pakai ulang untuk endpoint mana pun di modul ini. Endpoint-endpoint di modul ini mengikuti pola REST API yang sama, jadi begitu kamu paham satu, yang lain terasa familiar.
Empat jenis libur dan mana yang memotong saldo cuti
Supaya perhitungannya benar, kamu harus tahu bahwa tidak semua yang disebut “libur” berarti hari bebas kerja. API ini membedakan empat type, dan hanya dua di antaranya yang membebaskan karyawan dari potongan saldo. Dasar penetapannya mengikuti SKB 3 Menteri tentang hari libur nasional dan cuti bersama yang terbit tiap tahun.
| type | Arti | Memotong saldo cuti? |
|---|---|---|
Public Holiday |
Hari Libur Nasional | Tidak, ini hari bebas kerja |
Joint Holiday |
Cuti Bersama | Tidak, ini hari bebas kerja |
National Holiday |
Hari Peringatan Nasional | Ya, tetap hari kerja |
Observance |
Hari Peringatan | Ya, tetap hari kerja |
Jadi keputusannya jatuh pada type: Public Holiday dan Joint Holiday membebaskan saldo, sedangkan National Holiday dan Observance tetap dihitung hari kerja meski muncul di kalender sebagai peringatan. Gambar di bawah memetakan keempatnya ke keputusan itu, sekaligus menandai kenapa flag is_holiday tidak bisa dipakai sebagai penentu.

Siapkan API key gratis
Sebelum memanggil apa pun, kamu butuh API key. Kamu bisa daftar gratis lewat halaman API Kalender Indonesia, lalu menyalin API key dari dashboard. Key itulah yang kamu pasang di header x-api-co-id pada setiap panggilan, jadi simpan nilainya di tempat yang aman dan jangan menaruhnya di kode front-end. Modul libur ini gratis, jadi kamu tidak perlu mengisi saldo untuk mulai mencoba.
Ambil hari libur di rentang cuti dengan /check/range
Begitu kamu punya tanggal mulai dan selesai cuti dari form, kamu kirim keduanya ke /check/range sebagai start_date dan end_date (format YYYY-MM-DD), dengan rentang yang tidak boleh lebih dari satu tahun. Contoh untuk pengajuan Budi, cuti 1 sampai 3 Januari 2025:
curl -H "x-api-co-id: YOUR_API_KEY" \
"https://use.api.co.id/holidays/indonesia/check/range?start_date=2025-01-01&end_date=2025-01-03"
Balasannya memuat data berisi setiap libur yang jatuh di dalam rentang itu. Untuk rentang Budi, hanya ada satu:
{
"is_success": true,
"message": "Success",
"data": [
{
"id": 1,
"date": "2025-01-01",
"date_formatted": "1 Januari 2025",
"day_of_week": "Rabu",
"name": "Tahun Baru Masehi",
"type": "Public Holiday",
"year": 2025,
"is_today": false,
"is_upcoming": true,
"days_until": 44,
"is_holiday": true,
"is_joint_holiday": false,
"is_observance": false
}
]
}
Field yang kamu butuhkan untuk perhitungan cuti ada dua: date dan type. Sisanya cukup kamu teruskan apa adanya untuk ditampilkan ke pengguna. Kalau kamu cuma ingin menyaring cuti bersama saja, kamu bisa menambahkan parameter type, tapi parameter itu cocok persis satu nilai, jadi untuk Public dan Joint sekaligus lebih mudah menyaringnya di sisi kode seperti di bawah.
Tentukan tanggal yang tidak memotong saldo
Di sini banyak yang keliru: gampang berasumsi kalau is_holiday true berarti libur kerja, padahal tidak selalu. Pada objek libur, is_holiday bernilai true untuk Public Holiday maupun National Holiday, padahal National Holiday tetap hari kerja. Di endpoint /check/date, is_holiday tingkat atas bahkan bernilai true begitu ada libur apa pun pada tanggal itu, termasuk Observance yang juga tetap hari kerja. Penentu yang bisa kamu percaya adalah type. Bentuk himpunan tanggal libur kerja dari type Public Holiday dan Joint Holiday, lalu pakai himpunan itu untuk menghitung potongan:
const liburKerja = new Set(["Public Holiday", "Joint Holiday"]);
const tanggalLibur = new Set(
body.data
.filter((h) => liburKerja.has(h.type))
.map((h) => h.date)
);
function hitungHariDipotong(startDate, endDate, tanggalLibur) {
let potong = 0;
for (let d = new Date(startDate); d <= new Date(endDate); d.setDate(d.getDate() + 1)) {
const hari = d.getDay(); // 0 = Minggu, 6 = Sabtu
const iso = d.toISOString().slice(0, 10);
if (hari === 0 || hari === 6) continue; // akhir pekan tidak memotong
if (tanggalLibur.has(iso)) continue; // libur kerja tidak memotong
potong++;
}
return potong;
}
Yang perlu kamu perhatikan: akhir pekan dan libur kerja ditangani di dua baris continue yang terpisah, sehingga karyawan tidak kehilangan jatah cuti karena Sabtu, Minggu, atau tanggal merah yang kebetulan masuk rentang. Kalau zona waktu jadi isu di sisimu, ganti toISOString dengan format tanggal lokal supaya pergeseran hari tidak terjadi.
Contoh lengkap: hitung potongan cuti Budi 1-3 Januari 2025
Kita satukan semuanya dengan nilai yang sama dari awal. Budi mengajukan cuti 1 sampai 3 Januari 2025 (Rabu sampai Jumat), yang menurut kalender biasa terhitung tiga hari kerja. Setelah respons /check/range di atas kembali, kamu tinggal memanggil fungsinya:
const dipotong = hitungHariDipotong("2025-01-01", "2025-01-03", tanggalLibur);
// tanggalLibur = { "2025-01-01" }
// 1 Jan (Rabu) -> ada di tanggalLibur, tidak memotong
// 2 Jan (Kamis) -> memotong
// 3 Jan (Jumat) -> memotong
console.log(dipotong); // 2
Hasilnya saldo yang dipotong jadi 2 hari, sedangkan kalender polos menghitung 3. Selisih satu hari itu datang dari 1 Januari yang bertype Public Holiday. Angka itu yang kamu tampilkan di form cuti sebagai "cuti ini memotong 2 hari saldo". Nama dan tanggal di sini adalah nilai contoh dari dokumentasi; di produksi pakai apa yang dikembalikan API untuk tahun yang berjalan.
Galat yang akan kamu temui
Kalau requestmu salah, API membalas dengan is_success bernilai false dan data bernilai null, beserta pesan yang persis seperti ini, jadi kamu bisa meneruskannya langsung ke pengguna tanpa menebak-nebak:
{ "is_success": false, "message": "date range cannot exceed 1 year", "data": null }
Pesan lain yang muncul dari /check/range: start_date and end_date are required, invalid start_date format: use YYYY-MM-DD, invalid end_date format: use YYYY-MM-DD, dan end_date must be after start_date kalau rentangnya terbalik. Semuanya datang dengan status 400. Kalau header x-api-co-id hilang atau salah, kamu dapat 401; kalau batas laju terlampaui, 429; dan untuk error di sisi server, 500. Saat salah satu dari ini muncul, tahan dulu pemrosesan saldo dan tampilkan pesannya, supaya form tidak mengurangi jatah cuti dari data yang gagal diambil.
Endpoint lain yang tersedia
Pekerjaan validasi cuti cukup dengan /check/range. Kalau kamu butuh yang lain di modul yang sama, ini ringkasannya:
| Endpoint | Kegunaan |
|---|---|
GET / |
List libur dengan filter year, month, type, name, start_date/end_date, upcoming, page, size |
GET /:id |
Satu libur berdasarkan id |
GET /check/date |
Cek satu tanggal, mengembalikan is_weekend dan holidays[] |
GET /next |
Libur terdekat berikutnya |
GET /upcoming |
Daftar libur mendatang (limit default 10, maks 100) |
GET /stats |
Statistik libur per tahun |
GET /calendar |
Kalender libur per tahun atau bulan |
GET /years |
Daftar tahun yang tersedia |
GET /types |
Daftar jenis libur |
Harga, batas laju, dan cakupan tahun sebelum produksi
Modul ini gratis. Yang perlu kamu jaga cuma batas lajunya: 1.000 request per jam dan 10.000 request per hari per API key, dan lewat batas itu kamu dapat 429. Datanya mencakup tahun 2022 sampai 2026. Daftar libur satu tahun jarang berubah, jadi cara aman untuk tetap jauh di bawah batas adalah menyimpan hasil /check/range per tahun di cache sisimu dan memanggil API hanya saat tahunnya belum ada. Rincian tiap endpoint bisa kamu cek di dokumentasi resmi holidays.
Langkah berikutnya
Begitu validasi cuti jalan, backend HR yang sama bisa kamu sambungkan ke sistem absensi karyawan, supaya pengajuan cuti dan catatan kehadiran memakai kalender libur yang konsisten. Kalau produkmu soal pengiriman, kamu bisa memadukan kalender ini dengan data wilayah Indonesia dan perhitungan estimasi pengiriman untuk membuat kalkulator tanggal kirim yang otomatis melompati libur nasional. Data Indonesia lain seperti API kode pos juga jalan di gateway yang sama dengan satu API key.
Pertanyaan yang sering muncul
Apakah day_of_week selalu berbahasa Indonesia?
Di setiap objek libur dalam data[] atau holidays[], day_of_week berbahasa Indonesia seperti "Rabu". Khusus di /check/date, day_of_week tingkat atas berbahasa Inggris seperti "Wednesday", jadi perhatikan kamu membaca yang mana saat menampilkannya.
Kalau cuma perlu mengecek satu tanggal, endpoint mana yang dipakai?
Pakai GET /check/date?date=YYYY-MM-DD. Ia mengembalikan is_weekend dan array holidays[] untuk tanggal itu. Untuk keputusan memotong saldo, periksa type di dalam holidays[]; flag is_holiday di tingkat atas tidak bisa dipakai sebagai penentu.






