Alternatif API RajaOngkir yang paling cepat dipindahkan untuk kode cek ongkir adalah AgenWebsite Rate API: satu endpoint POST /v1/rates, autentikasi lewat header x-api-key, dan paket Free 150 request per hari tanpa kartu kredit. Yang membuat developer mulai mencari pengganti biasanya bukan soal harga, tapi soal kode yang tiba-tiba harus dirombak: base URL berubah, header berubah, format lokasi berubah, dan integrasi yang sudah jalan dua tahun mendadak perlu disentuh lagi.

Saya menulis ini dari sisi tim yang membangun Rate API dan membaca tiket support-nya. Jadi artikel ini sengaja tidak netral, tapi saya usahakan jujur. Ada kasus di mana API kami memang pas untuk menggantikan RajaOngkir, dan ada kasus di mana Anda sebaiknya belum pindah. Dua-duanya saya tulis.

💡 Jawaban Singkat: Kalau Anda butuh alternatif API RajaOngkir untuk cek ongkir J&T Express, SPX, Lion Parcel, SAP Express, atau J&T Cargo, AgenWebsite Rate API bisa dipakai mulai gratis (150 request per hari) lewat satu endpoint POST /v1/rates dengan header x-api-key. Migrasinya biasanya cukup mengganti base URL, header, dan nama field lokasi. Untuk JNE, POS, atau TIKI, versi 1 belum mendukung, jadi jangan pindah dulu kalau kurir itu wajib.

Kenapa banyak developer mencari alternatif API RajaOngkir?

Pemicunya adalah perubahan versi. Di dokumentasi resmi rajaongkir.com/docs, contoh request sekarang mengarah ke https://rajaongkir.komerce.id/api/v1/..., API key dikirim lewat header bernama key, dan pencarian lokasi tujuan memakai endpoint /destination/domestic-destination. Dokumen yang sama menyebut rate limit “berdasarkan tier langganan” tanpa angka di halaman pengantar. RajaOngkir sendiri sudah berada di bawah Komerce sejak 2024.

Artinya, kode yang Anda tulis untuk versi lama tidak otomatis cocok dengan versi baru. Kalau Anda memang harus membuka file integrasi dan menulis ulang pemanggilan HTTP-nya, wajar kalau muncul pertanyaan: sekalian saja dievaluasi, API mana yang paling masuk akal untuk dua tahun ke depan?

Developer yang sudah sampai di titik itu biasanya tidak butuh daftar fitur, tapi dua hal: apakah API penggantinya cocok untuk kurir yang mereka pakai, dan berapa baris kode yang harus diubah. Dua hal itu yang saya jawab di bawah.

Apa yang perlu dicek sebelum pindah API cek ongkir?

Sebelum membandingkan harga langganan, cek enam hal ini terhadap toko atau aplikasi yang Anda kerjakan. Kalau ada satu yang tidak cocok, migrasi akan macet di tengah jalan.

  1. Kurir yang benar-benar dipakai. Buka laporan pengiriman tiga bulan terakhir. Yang sering kejadian: toko merasa “butuh semua kurir”, padahal 90% paket berangkat lewat J&T dan SPX.
  2. Kuota harian riil. Hitung berapa kali kode Anda memanggil API per hari, bukan berapa order yang masuk. Satu halaman checkout bisa memanggil cek ongkir 3 sampai 5 kali kalau pelanggan gonta-ganti alamat.
  3. Format lokasi. Kode lama Anda menyimpan ID kota/kecamatan dari API lama? Atau kode pos? Ini menentukan apakah Anda perlu tabel pemetaan atau cukup ganti nama field.
  4. Satuan berat. Gram atau kilogram, integer atau desimal. Salah di sini, ongkir Anda meleset 1.000 kali lipat dan baru ketahuan saat pelanggan komplain.
  5. Diskon atau cashback. Apakah Anda butuh tarif publik saja, atau tarif yang sudah dipotong diskon kurir untuk ditampilkan ke pembeli?
  6. Kebutuhan AWB, COD, dan tracking. Kalau integrasi Anda juga membuat resi atau mengecek status paket, API yang hanya menyediakan tarif tidak cukup menggantikan.

Poin keenam yang paling sering dilewatkan, dan paling mahal kalau ketahuan belakangan.

Kapan AgenWebsite Rate API cocok, dan kapan belum?

Rate API cocok kalau yang Anda butuhkan adalah tarif ongkir reguler (non-COD) dari J&T Express, Lion Parcel, SAP Express, SPX Express, dan J&T Cargo, lengkap dengan field diskon dan cashback. Belum cocok kalau Anda wajib menampilkan JNE, POS, atau TIKI, atau kalau kode Anda juga membuat resi. Tabel ini saya tulis apa adanya.

Kebutuhan Anda Rate API v1 Catatan
Cek ongkir J&T, SPX, Lion Parcel, SAP, J&T Cargo Cocok Lima kurir ini yang tersedia di v1. Kode: jnt, spx, lion, sap, jtc.
Cek ongkir JNE, POS Indonesia, TIKI, SiCepat Belum Tidak ada di v1. Jangan pindah dulu kalau ini wajib.
Tarif dengan diskon dan cashback Cocok Field discount, discounted_cost, cashback ada di respons, termasuk di paket Free.
Membuat resi (AWB) atau COD lewat API Belum v1 hanya tarif. Pembuatan order dan AWB ada di roadmap, belum tersedia.
Tracking status paket lewat API Belum Tidak ada endpoint tracking di v1.
Volume di bawah 150 request per hari Cocok, gratis Paket Free, 20 request per menit, reset 00:00 WIB.
Volume ribuan request per hari Cocok, berbayar Pro Rp129.000/bulan untuk 30.000 request/hari, Max Rp229.000/bulan untuk 60.000 request/hari.
Satu respons terurut cheapest/fastest lintas kurir Cocok Parameter sort dan flag cheapest/fastest di tiap tarif.

Kalau toko Anda mengirim lewat JNE setiap hari, artikel ini belum untuk Anda. Simpan saja dan cek lagi saat kurirnya bertambah. Harga di tabel saya ambil dari halaman produk Rate API saat artikel ini ditulis; cek ulang di sana karena halaman produk yang jadi acuan, bukan artikel ini.

Bagaimana memetakan request dan response RajaOngkir ke Rate API?

Secara konsep, kedua API menerima asal, tujuan, berat, dan kurir, lalu mengembalikan daftar layanan beserta ongkos dan estimasi. Perbedaannya di bentuk: Rate API memakai body JSON dengan objek shipper dan destination, dan respons sudah dinormalisasi lintas kurir, jadi Anda tidak perlu merapikan struktur per kurir sendiri.

Tabel berikut memetakan konsepnya. Kolom kiri saya tulis sebagai konsep, bukan nama parameter persis dari RajaOngkir, karena nama field di sisi mereka bisa berbeda antar versi dan Anda sebaiknya mencocokkan dengan dokumentasi resmi yang Anda pakai.

Konsep di kode lama Di AgenWebsite Rate API Keterangan
Base URL vendor lama https://api.agenwebsite.com/v1 Sandbox: https://api-sandbox.agenwebsite.com/v1 dengan key awk_test_.
Header API key (di RajaOngkir V2: key) x-api-key: awk_live_xxxx Tanpa OAuth, tanpa bearer token.
Endpoint hitung ongkir POST /v1/rates, body JSON Pastikan Content-Type: application/json.
ID kota/kecamatan asal shipper.postal_code atau shipper.subdistrict_id Kirim tepat satu. Keduanya atau tidak sama sekali = 400 validation_error.
ID kota/kecamatan tujuan destination.postal_code atau destination.subdistrict_id Struktur sama dengan shipper. ID kecamatan dicari lewat GET /v1/locations/search.
Berat weight (integer, gram, min 1) Kalau kode lama menyimpan kg, kalikan 1.000 dan bulatkan ke atas.
Kode kurir (string dipisah koma) couriers (array), contoh ["jnt","spx"] Opsional. Kosongkan untuk semua kurir yang tersedia.
Nilai barang untuk asuransi item_value (IDR) Hanya memengaruhi insurance.fee, bukan ongkir.
Hasil per kurir → nama layanan data.rates[].service_name, service_code Satu array datar untuk semua kurir.
Hasil per kurir → biaya cost, discounted_cost, cost_formatted Tampilkan discounted_cost kalau Anda ingin harga setelah diskon.
Hasil per kurir → estimasi etd_min_days, etd_max_days, etd_text Plus estimated_delivery.from/to berupa tanggal.
Cari termurah sendiri di kode sort: "cheapest" dan flag cheapest: true Atau data.cheapest langsung.

Detail yang sering bikin developer kaget waktu uji coba pertama: weight harus integer. Mengirim 1.5 karena kebiasaan kilogram akan ditolak sebagai validation_error, bukan dibulatkan diam-diam. Itu sengaja; lebih baik error di staging daripada ongkir salah di produksi.

Spesifikasi lengkap request dan response ada di dokumentasi POST /v1/rates.

Contoh kode migrasi: PHP/Laravel dan Node.js

Langkah migrasinya pendek. Simpan key di variabel lingkungan server, ganti base URL dan header, ubah bentuk body ke JSON dengan objek shipper/destination, lalu sesuaikan pembacaan respons ke data.rates. Di bawah ini versi sebelum dan sesudah untuk Laravel, lalu Node.js.

1. Simpan key di sisi server

Ini bukan saran kosmetik. Key awk_live_ yang ikut terkirim ke browser bisa diambil siapa pun dari tab Network, lalu kuota harian Anda habis dipakai orang lain. Semua contoh di bawah membaca key dari .env.

# .env (server, jangan di-commit)
AGENWEBSITE_RATE_API_KEY=awk_live_xxxxxxxxxxxxxxxx
// config/services.php (Laravel)
'agenwebsite' => [
    'key'      => env('AGENWEBSITE_RATE_API_KEY'),
    'base_url' => env('AGENWEBSITE_RATE_API_URL', 'https://api.agenwebsite.com/v1'),
],

2. Laravel: sebelum dan sesudah

Pola “sebelum” di bawah adalah bentuk umum integrasi RajaOngkir V2 yang kami lihat di kode klien: header key, form field asal/tujuan/berat/kurir. Path endpoint biaya saya biarkan sebagai variabel karena Anda harus mencocokkannya dengan dokumentasi RajaOngkir yang Anda pakai.

<?php
// SEBELUM (pola umum RajaOngkir V2, disederhanakan)
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
        'key' => config('services.rajaongkir.key'),
    ])
    ->asForm()
    ->post('https://rajaongkir.komerce.id/api/v1' . $costPath, [
        'origin'      => $originId,       // ID lokasi dari API lama
        'destination' => $destinationId,
        'weight'      => $weightGram,
        'courier'     => 'jnt:sicepat',
    ]);

$results = $response->json('data', []);
// lalu Anda biasanya loop per kurir dan merapikan strukturnya sendiri
<?php
// SESUDAH (AgenWebsite Rate API)
namespace App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class ShippingRateService
{
    public function rates(string $originPostal, string $destPostal, int $weightGram, array $couriers = []): array
    {
        $cacheKey = "rates:{$originPostal}:{$destPostal}:{$weightGram}:" . implode(',', $couriers);

        return Cache::remember($cacheKey, now()->addMinutes(30), function () use ($originPostal, $destPostal, $weightGram, $couriers) {
            $response = Http::withHeaders([
                    'x-api-key'    => config('services.agenwebsite.key'),
                    'Content-Type' => 'application/json',
                ])
                ->timeout(15)
                ->post(config('services.agenwebsite.base_url') . '/rates', [
                    'shipper'     => ['postal_code' => $originPostal],
                    'destination' => ['postal_code' => $destPostal],
                    'weight'      => $weightGram,          // gram, integer
                    'couriers'    => $couriers ?: null,    // null = semua kurir v1
                    'sort'        => 'cheapest',
                ]);

            $body = $response->json();

            if (! $response->successful() || ! ($body['success'] ?? false)) {
                $code = $body['error']['code'] ?? 'unknown';
                $reqId = $body['error']['request_id'] ?? '-';
                throw new \RuntimeException("Rate API gagal: {$code} (request_id {$reqId})");
            }

            return $body['data']['rates'];
        });
    }
}

Perhatikan tiga hal: body dikirim sebagai JSON (bukan form), lokasi masuk ke objek shipper/destination, dan pengecekan success dilakukan dari body, bukan hanya dari status HTTP. Yang terakhir itu saran dari dokumentasi error kami sendiri.

3. Node.js: sebelum dan sesudah

// SEBELUM (pola umum RajaOngkir V2, disederhanakan)
const res = await fetch(`https://rajaongkir.komerce.id/api/v1${costPath}`, {
  method: "POST",
  headers: {
    key: process.env.RAJAONGKIR_KEY,
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body: new URLSearchParams({
    origin: originId,
    destination: destinationId,
    weight: String(weightGram),
    courier: "jnt:sicepat",
  }),
});
const results = (await res.json()).data;
// SESUDAH (AgenWebsite Rate API), dijalankan di server, bukan di browser
const BASE_URL = process.env.AGENWEBSITE_RATE_API_URL ?? "https://api.agenwebsite.com/v1";

export async function getRates({ originPostal, destPostal, weightGram, couriers = [] }) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 15_000);

  try {
    const res = await fetch(`${BASE_URL}/rates`, {
      method: "POST",
      headers: {
        "x-api-key": process.env.AGENWEBSITE_RATE_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        shipper: { postal_code: originPostal },
        destination: { postal_code: destPostal },
        weight: Math.ceil(weightGram),   // integer, gram
        couriers: couriers.length ? couriers : undefined,
        sort: "cheapest",
      }),
      signal: controller.signal,
    });

    const body = await res.json();

    if (!res.ok || !body.success) {
      const err = body.error ?? {};
      throw new Error(`Rate API gagal: ${err.code ?? res.status} (request_id ${err.request_id ?? "-"})`);
    }

    return body.data.rates;
  } finally {
    clearTimeout(timer);
  }
}

Kalau aplikasi Anda Next.js, panggil fungsi ini dari Route Handler atau Server Action, jangan dari komponen client. Alasannya sama: key harus tetap di server.

4. Bentuk respons yang akan Anda baca

Contoh respons disederhanakan, hanya field yang biasanya dipakai di checkout:

{
  "success": true,
  "currency": "IDR",
  "data": {
    "rates": [
      {
        "courier_code": "jnt",
        "courier_name": "J&T Express",
        "service_code": "EZ",
        "service_name": "Reguler",
        "cost": 12000,
        "discount": 2000,
        "discounted_cost": 10000,
        "cashback": 0,
        "etd_min_days": 2,
        "etd_max_days": 3,
        "etd_text": "2-3 hari",
        "cheapest": true,
        "fastest": false
      }
    ]
  },
  "rate_limit": { "limit": 150, "remaining": 149, "reset_at": "..." }
}

Nilai dan kode layanan di contoh ini hanya ilustrasi bentuk. Nama field-nya yang penting, dan itu sesuai dokumentasi.

Panduan integrasi yang lebih panjang, termasuk versi WooCommerce, ada di artikel cara integrasi API ongkir di WooCommerce, Laravel, dan Node.js.

Bagaimana menangani error dan kuota setelah pindah?

Semua error Rate API memakai amplop yang sama: success: false, lalu objek error dengan code, message, dan request_id. Simpan request_id di log Anda; itu yang kami minta pertama kali kalau Anda menghubungi support.

HTTP error.code Yang biasanya terjadi di kode migrasi
400 validation_error weight bukan integer, atau mengirim postal_code dan subdistrict_id sekaligus. Cek error.field.
401 unauthorized Header masih bernama key (sisa kode lama), key kosong, atau key sudah dicabut.
404 shipper_not_found / destination_not_found Kode pos tidak dikenali, atau Anda mengirim ID lokasi dari API lama sebagai subdistrict_id. ID-nya tidak sama.
422 unsupported_courier Array couriers masih berisi jne atau sicepat. Di v1 hanya jnt, lion, sap, spx, jtc.
429 rate_limit_exceeded Kuota harian habis atau burst per menit terlewati. Baca Retry-After (detik) sebelum mencoba lagi.
502 courier_upstream_error Semua kurir gagal merespons. Kalau hanya sebagian yang gagal, Anda tetap dapat 200 dengan meta.partial: true dan daftar couriers_failed.

Untuk kuota, setiap respons membawa header X-RateLimit-Limit, X-RateLimit-Remaining, dan X-RateLimit-Reset. Kuota dihitung per akun per hari, dibagi ke semua key yang Anda punya, dan direset pukul 00:00 WIB. Kalau Anda ingin memantau tanpa menebak, GET /v1/usage mengembalikan plan, limit, terpakai, dan sisa untuk hari itu.

Cara paling murah menjaga kuota adalah cache. Kombinasi asal + tujuan + berat + kurir hampir tidak berubah dalam 30 menit, dan di toko dengan 30-40 order per hari, cache seperti pada contoh Laravel di atas biasanya memotong panggilan API jauh di bawah batas Free. Pembahasan lebih dalam soal ini ada di artikel rate limiting dan kuota API ongkir.

Bagaimana dengan Binderbyte dan opsi lain?

Sebagai opsi lain, Binderbyte, Biteship, atau KiriminAja punya cakupan dan model harga yang berbeda, dan beberapa di antaranya menyediakan hal yang belum ada di Rate API v1 seperti tracking atau pembuatan resi. Kalau kebutuhan Anda ada di sana, bandingkan dulu lewat artikel perbandingan API ongkir AgenWebsite, RajaOngkir, dan Binderbyte sebelum menulis satu baris kode pun.

Pertanyaan yang Sering Diajukan

Apakah API cek ongkir AgenWebsite gratis?

Ya, paket Free memberi 150 request per hari dan 20 request per menit tanpa kartu kredit, dan key-nya bisa dibuat sendiri setelah verifikasi email. Field diskon dan cashback ikut tersedia di paket Free. Kalau volume Anda lebih besar, ada Pro (Rp129.000/bulan, 30.000 request/hari) dan Max (Rp229.000/bulan, 60.000 request/hari).

Kurir apa saja yang didukung, dan kenapa /v1/couriers hanya menampilkan lima?

Versi 1 mendukung J&T Express (jnt), Lion Parcel (lion), SAP Express (sap), SPX Express (spx), dan J&T Cargo (jtc). GET /v1/couriers menampilkan lima karena memang itu yang tersedia di API saat ini; jumlah kurir di plugin AgenWebsite Shipping untuk WooCommerce berbeda dan tidak berlaku untuk Rate API. JNE, POS, TIKI, dan SiCepat belum ada di v1.

Bisakah Rate API membuat resi (AWB) atau COD?

Belum. Rate API v1 hanya menyediakan tarif pengiriman reguler non-COD. Pembuatan order dan AWB ada di roadmap, tapi kalau integrasi Anda hari ini butuh resi lewat API, Rate API belum bisa menggantikan bagian itu.

Bisakah satu akun memakai beberapa API key?

Bisa. Paket Free mengizinkan 2 key aktif, Pro 5, dan Max 20, misalnya satu untuk staging dan satu untuk produksi. Yang perlu diingat, kuota harian dihitung per akun dan dibagi ke semua key, bukan per key.

Kenapa muncul error lokasi tidak ditemukan (shipper_not_found / destination_not_found)?

Penyebab paling umum saat migrasi adalah mengirim ID lokasi dari API lama ke field subdistrict_id; ID-nya tidak sama antar vendor. Cari ulang ID kecamatan lewat GET /v1/locations/search, atau pakai postal_code saja. Pastikan juga hanya satu dari dua field itu yang dikirim untuk tiap pihak.

Bisakah dipakai di Laravel, Node.js, atau Next.js?

Bisa, karena ini REST biasa dengan body JSON dan header x-api-key; tidak ada SDK yang wajib dipasang. Satu syaratnya: panggil dari sisi server (controller Laravel, Route Handler atau Server Action di Next.js), bukan dari JavaScript di browser, supaya key tidak bocor.

Jadi, mulai dari mana?

Buka laporan kurir tiga bulan terakhir dulu. Kalau mayoritas paket Anda lewat J&T, SPX, Lion Parcel, SAP, atau J&T Cargo dan Anda hanya butuh tarif, migrasinya realistis selesai dalam satu sore: ganti header, ganti base URL, bungkus lokasi ke shipper/destination, pastikan berat integer gram, lalu uji di sandbox dengan key awk_test_. Kalau JNE atau resi masih wajib, tandai artikel ini dan tunggu sampai kurirnya bertambah; memaksakan migrasi sekarang hanya akan membuat Anda menjalankan dua API sekaligus.

👉 Key Free bisa dibuat langsung di halaman AgenWebsite Rate API, dan 150 request per hari cukup untuk menguji seluruh alur checkout sebelum memutuskan.