Dokumentasi API
Pesan Cek Plagiasi dan Cek AI Turnitin dari aplikasimu sendiri. Lengkap dengan parameter, contoh respons, dan contoh kode.
Pengantar
Dengan API ini kamu bisa memesan Cek Plagiasi Turnitin dan Cek AI Turnitin langsung dari aplikasimu sendiri: bot, website toko, atau skrip otomatis, tanpa perlu membuka dashboard setiap kali. API aktif selama add on Akses API Key di akunmu masih berlaku.
__BASE__/api/v1JSON (kecuali unggah PDF dan unduh hasil)Tidak ada webhook. Setelah mengirim order, kamu memantau statusnya dengan polling sampai done, lalu mengunduh PDF hasilnya.
Mulai dari mana
- Aktifkan add on. Beli add on Akses API Key di menu Add On pada dashboard. Bisa dibayar dari saldo.
- Buat API key. Buka halaman API key, beri nama, lalu salin key yang muncul. Key hanya tampil sekali.
- Isi saldo. Order lewat API memotong saldo akunmu. Top up di dashboard bila perlu.
- Kirim order pertamamu. Salin fungsi pada bagian Klien siap pakai, lalu ikuti contoh di bagian Buat order.
Autentikasi
Setiap request wajib membawa API key. Pilih salah satu header:
| Header | Isi |
|---|---|
Authorization | Bearer zs_live_xxxxxxxx (disarankan) |
X-API-Key | zs_live_xxxxxxxx |
Aturan key
- Key dibuat di halaman API key. Key asli hanya tampil sekali, simpan di tempat aman.
- Maksimal 2 key aktif per akun. Key yang dicabut langsung tidak berlaku.
- Simpan key di server, jangan di kode yang berjalan di browser atau aplikasi mobile.
- Allowlist IP (opsional): jika diisi, request dari IP lain ditolak dengan
403 ip_not_allowed. - Jika add on berakhir, semua key ditolak dengan
403 addon_expired. Key aktif lagi setelah add on diperpanjang.
Batas request
| Batas | Nilai | Jika terlewati |
|---|---|---|
| Per key | 30 request / menit | 429 rate_limited dan header Retry-After |
| Per IP | 300 request / menit | 429 too_many_requests |
| Key salah | 10 kali / menit / IP | 429 too_many_requests |
| Order aktif | 10 order API bersamaan | 429 too_many_active_orders |
| Order per hari | 100 order API per 24 jam | 429 daily_order_limit |
Klien siap pakai
Contoh di halaman ini memakai fungsi kecil zstore() yang menambahkan header autentikasi. Simpan key di environment variable ZSTORE_KEY, lalu salin fungsi ini ke proyekmu.
<?php
const ZSTORE_BASE = '__BASE__/api/v1';
// Mengembalikan ['status' => int, 'body' => string]
function zstore(string $method, string $path, ?string $body = null, array $headers = []): array
{
$headers[] = 'Authorization: Bearer ' . getenv('ZSTORE_KEY');
$ctx = stream_context_create(['http' => [
'method' => $method,
'header' => implode("\r\n", $headers),
'content' => $body ?? '',
'ignore_errors' => true, // tetap baca body saat HTTP 4xx / 5xx
'timeout' => 60,
]]);
$raw = file_get_contents(ZSTORE_BASE . $path, false, $ctx);
preg_match('#HTTP/\S+\s+(\d{3})#', $http_response_header[0] ?? '', $m);
return ['status' => (int) ($m[1] ?? 0), 'body' => $raw === false ? '' : $raw];
}
// Node.js 18 atau lebih baru (fetch bawaan)
const BASE = '__BASE__/api/v1';
const KEY = process.env.ZSTORE_KEY;
// Mengembalikan objek Response. Pakai res.status, await res.json(), atau await res.arrayBuffer()
function zstore(method, path, { body, headers } = {}) {
return fetch(BASE + path, {
method,
body,
headers: { Authorization: `Bearer ${KEY}`, ...headers },
});
}
import os
import requests
BASE = "__BASE__/api/v1"
session = requests.Session()
session.headers["Authorization"] = "Bearer " + os.environ["ZSTORE_KEY"]
# Mengembalikan objek Response. Pakai r.status_code, r.json(), atau r.content
def zstore(method, path, **kwargs):
return session.request(method, BASE + path, timeout=60, **kwargs)
Daftar endpoint
| Method | Path | Fungsi |
|---|---|---|
| GET | /balance | Cek saldo akun |
| GET | /price | Harga dan batas tiap produk |
| POST | /order | Buat order (unggah PDF) |
| GET | /orders | Daftar order milik akun |
| GET | /order?id= | Detail satu order |
| GET | /order/download?id= | Unduh PDF hasil |
| POST | /order/cancel?id= | Batalkan order dan kembalikan saldo |
Semua path di atas berada di bawah Base URL, misalnya __BASE__/api/v1/balance.
Cek saldo
/balanceMengembalikan saldo akun yang dipakai untuk membayar order.Respons 200
{
"ok": true,
"username": "budi",
"balance": 125000
}
$r = zstore('GET', '/balance');
$data = json_decode($r['body'], true);
echo $data['balance'];
const res = await zstore('GET', '/balance');
const data = await res.json();
console.log(data.balance);
data = zstore("GET", "/balance").json()
print(data["balance"])
Harga produk
/priceHarga dan batas tiap produk. Dipakai untuk menghitung biaya sebelum membuat order.Parameter query
| Nama | Wajib | Isi |
|---|---|---|
product | Tidak | turnitin (bawaan) atau turnitin-ai |
Respons 200
{
"ok": true,
"product": "Turnitin Check",
"id": "turnitin",
"price": 3000,
"currency": "IDR",
"products": [
{ "id": "turnitin", "name": "Turnitin Check", "price": 3000 },
{ "id": "turnitin-ai", "name": "Cek AI Turnitin", "price": 25000, "maxWords": 30000 }
]
}
Kolom products berisi semua produk yang bisa dipesan. maxWords adalah batas kata per dokumen untuk Cek AI.
$r = zstore('GET', '/price?product=turnitin-ai');
$data = json_decode($r['body'], true);
echo $data['price'], ' ', $data['currency'];
const res = await zstore('GET', '/price?product=turnitin-ai');
const data = await res.json();
console.log(data.price, data.currency);
data = zstore("GET", "/price", params={"product": "turnitin-ai"}).json()
print(data["price"], data["currency"])
Buat order
/orderMengunggah PDF dan membuat order. Saldo langsung dipotong sesuai harga produk.Header
| Header | Wajib | Isi |
|---|---|---|
Content-Type | Ya | application/pdf |
Idempotency-Key | Ya | String unik 8 sampai 64 karakter (huruf, angka, _, -). Satu key untuk satu file. |
Body
Isi file PDF mentah (bukan multipart, bukan base64). Ukuran maksimal 4 MB.
Parameter query
| Nama | Berlaku | Isi | Bawaan |
|---|---|---|---|
product | Semua | turnitin atau turnitin-ai | turnitin |
filename | Semua | Nama file yang tampil di order dan hasil | document.pdf |
class_type | turnitin | classic atau new | classic |
exclude_bibliography | turnitin | true / false | true |
exclude_quotes | turnitin | true / false | true |
exclude_cited | turnitin | true / false (hanya mode new) | true |
exclude_small_matches | turnitin | true / false | false |
small_match_value | turnitin | Angka. 0 berarti nilai standar | 0 |
small_match_unit | turnitin | kata atau persen | kata |
allow_deferred | turnitin | Jika kuota harian penuh, proses setelah 00.00 WIB | false |
Cek AI (product=turnitin-ai) tidak memakai filter, class_type, maupun kuota harian. Dokumen dibatasi jumlah kata sesuai maxWords dari /price.
Respons 201: Cek Plagiasi
{
"ok": true,
"orderId": "TRN-1791697852995-B24A459A",
"product": "turnitin",
"status": "paid",
"price": 3000,
"balance": 122000,
"deferred": false
}
Respons 201: Cek AI
{
"ok": true,
"orderId": "TRN-AI-1791708356221-4AEC3E80",
"product": "turnitin-ai",
"status": "manual_process",
"price": 25000,
"balance": 97000,
"deferred": false,
"wordCount": 12840
}
Respons 200: permintaan diulang
Jika Idempotency-Key yang sama dikirim lagi dengan file dan produk yang sama, API mengembalikan order yang sudah ada dan tidak memotong saldo lagi.
{
"ok": true,
"idempotent": true,
"orderId": "TRN-1791697852995-B24A459A",
"status": "processing"
}
Contoh: Cek Plagiasi
$pdf = file_get_contents('skripsi.pdf');
$query = http_build_query([
'filename' => 'skripsi.pdf',
'class_type' => 'classic',
'exclude_bibliography' => 'true',
'exclude_quotes' => 'true',
]);
$r = zstore('POST', '/order?' . $query, $pdf, [
'Content-Type: application/pdf',
'Idempotency-Key: skripsi-2026-0001',
]);
$data = json_decode($r['body'], true);
if ($r['status'] === 201 || $r['status'] === 200) {
echo 'Order: ', $data['orderId'], PHP_EOL;
} else {
echo 'Gagal: ', $data['error'], PHP_EOL;
}
import fs from 'node:fs';
const query = new URLSearchParams({
filename: 'skripsi.pdf',
class_type: 'classic',
exclude_bibliography: 'true',
exclude_quotes: 'true',
});
const res = await zstore('POST', `/order?${query}`, {
body: fs.readFileSync('skripsi.pdf'),
headers: {
'Content-Type': 'application/pdf',
'Idempotency-Key': 'skripsi-2026-0001',
},
});
const data = await res.json();
if (res.status === 201 || res.status === 200) {
console.log('Order:', data.orderId);
} else {
console.error('Gagal:', data.error);
}
with open("skripsi.pdf", "rb") as f:
pdf = f.read()
r = zstore(
"POST", "/order",
params={
"filename": "skripsi.pdf",
"class_type": "classic",
"exclude_bibliography": "true",
"exclude_quotes": "true",
},
data=pdf,
headers={
"Content-Type": "application/pdf",
"Idempotency-Key": "skripsi-2026-0001",
},
)
data = r.json()
if r.status_code in (200, 201):
print("Order:", data["orderId"])
else:
print("Gagal:", data["error"])
Contoh: Cek AI
$r = zstore('POST', '/order?product=turnitin-ai&filename=skripsi.pdf',
file_get_contents('skripsi.pdf'),
['Content-Type: application/pdf', 'Idempotency-Key: skripsi-ai-0001']
);
$data = json_decode($r['body'], true);
echo $data['orderId'], ' (', $data['wordCount'] ?? '-', ' kata)';
const res = await zstore('POST', '/order?product=turnitin-ai&filename=skripsi.pdf', {
body: fs.readFileSync('skripsi.pdf'),
headers: { 'Content-Type': 'application/pdf', 'Idempotency-Key': 'skripsi-ai-0001' },
});
const data = await res.json();
console.log(data.orderId, `(${data.wordCount ?? '-'} kata)`);
r = zstore(
"POST", "/order",
params={"product": "turnitin-ai", "filename": "skripsi.pdf"},
data=open("skripsi.pdf", "rb").read(),
headers={"Content-Type": "application/pdf", "Idempotency-Key": "skripsi-ai-0001"},
)
data = r.json()
print(data["orderId"], "(%s kata)" % data.get("wordCount", "-"))
Daftar order
/ordersDaftar order milik akun, terbaru lebih dulu.Parameter query
| Nama | Isi | Bawaan |
|---|---|---|
limit | Jumlah data, 1 sampai 50 | 20 |
offset | Lewati sejumlah data awal | 0 |
product | turnitin atau turnitin-ai untuk menyaring. Kosong berarti semua. | - |
Respons 200
{
"ok": true,
"total": 2,
"limit": 20,
"offset": 0,
"orders": [
{
"orderId": "TRN-AI-1791708356221-4AEC3E80",
"product": "turnitin-ai",
"status": "done",
"statusMsg": "Selesai.",
"fileName": "skripsi.pdf",
"price": 25000,
"createdAt": "2026-10-11T12:45:56.221Z",
"paidAt": "2026-10-11T12:45:56.221Z",
"doneAt": "2026-10-11T13:02:10.000Z",
"resultReady": true,
"wordCount": 12840
},
{
"orderId": "TRN-1791697852995-B24A459A",
"product": "turnitin",
"status": "done",
"statusMsg": "Similarity 12%",
"fileName": "skripsi.pdf",
"price": 3000,
"createdAt": "2026-10-11T09:50:52.995Z",
"paidAt": "2026-10-11T09:50:52.995Z",
"doneAt": "2026-10-11T10:03:41.000Z",
"resultReady": true,
"classType": "classic",
"similarity": 12
}
]
}
$r = zstore('GET', '/orders?limit=20&product=turnitin-ai');
foreach (json_decode($r['body'], true)['orders'] as $o) {
echo $o['orderId'], ' ', $o['status'], PHP_EOL;
}
const res = await zstore('GET', '/orders?limit=20&product=turnitin-ai');
const { orders } = await res.json();
for (const o of orders) console.log(o.orderId, o.status);
r = zstore("GET", "/orders", params={"limit": 20, "product": "turnitin-ai"})
for o in r.json()["orders"]:
print(o["orderId"], o["status"])
Detail order
/orderStatus dan hasil satu order. Dipakai untuk polling.Parameter query
| Nama | Wajib | Isi |
|---|---|---|
id | Ya | ID order, contoh TRN-AI-1791708356221-4AEC3E80 |
Respons 200
{
"ok": true,
"order": {
"orderId": "TRN-1791697852995-B24A459A",
"product": "turnitin",
"status": "done",
"statusMsg": "Similarity 12%",
"fileName": "skripsi.pdf",
"price": 3000,
"createdAt": "2026-10-11T09:50:52.995Z",
"paidAt": "2026-10-11T09:50:52.995Z",
"doneAt": "2026-10-11T10:03:41.000Z",
"resultReady": true,
"classType": "classic",
"similarity": 12
}
}
Kolom order
| Kolom | Isi |
|---|---|
orderId | ID order. Cek AI berawalan TRN-AI-, Cek Plagiasi berawalan TRN- |
product | turnitin atau turnitin-ai |
status | Lihat tabel status di bawah |
statusMsg | Keterangan status dalam bahasa Indonesia |
resultReady | true jika PDF hasil sudah bisa diunduh |
similarity | Persentase kemiripan. Hanya untuk Cek Plagiasi yang selesai, selain itu null |
classType | Tampilan laporan. Hanya untuk Cek Plagiasi |
wordCount | Jumlah kata dokumen. Hanya untuk Cek AI |
createdAt, paidAt, doneAt | Waktu format ISO 8601 (UTC). null jika belum terjadi |
$r = zstore('GET', '/order?id=' . urlencode('TRN-1791697852995-B24A459A'));
$order = json_decode($r['body'], true)['order'];
echo $order['status'], ' ', $order['resultReady'] ? 'siap diunduh' : 'belum siap';
const res = await zstore('GET', '/order?id=TRN-1791697852995-B24A459A');
const { order } = await res.json();
console.log(order.status, order.resultReady ? 'siap diunduh' : 'belum siap');
r = zstore("GET", "/order", params={"id": "TRN-1791697852995-B24A459A"})
order = r.json()["order"]
print(order["status"], "siap diunduh" if order["resultReady"] else "belum siap")
Unduh hasil
/order/downloadMengunduh PDF hasil. Hanya bisa saat resultReady bernilai true.Parameter query
| Nama | Wajib | Isi |
|---|---|---|
id | Ya | ID order |
Respons
Sukses mengembalikan 200 dengan Content-Type: application/pdf (isi file langsung, bukan JSON). Jika belum siap, respons 409 berikut:
{
"ok": false,
"error": "not_ready",
"status": "processing"
}
$r = zstore('GET', '/order/download?id=' . urlencode($orderId));
if ($r['status'] === 200) {
file_put_contents('hasil.pdf', $r['body']);
} else {
echo 'Belum bisa diunduh: ', json_decode($r['body'], true)['error'];
}
import fs from 'node:fs';
const res = await zstore('GET', `/order/download?id=${orderId}`);
if (res.status === 200) {
fs.writeFileSync('hasil.pdf', Buffer.from(await res.arrayBuffer()));
} else {
console.error('Belum bisa diunduh:', (await res.json()).error);
}
r = zstore("GET", "/order/download", params={"id": order_id})
if r.status_code == 200:
with open("hasil.pdf", "wb") as f:
f.write(r.content)
else:
print("Belum bisa diunduh:", r.json()["error"])
Batalkan order
/order/cancelMembatalkan order yang masih menunggu 00.00 WIB dan mengembalikan saldo penuh ke akunmu.Parameter query
| Nama | Wajib | Isi |
|---|---|---|
id | Ya | ID order buatan API milik akunmu |
Kapan bisa dibatalkan
| Status order | Bisa dibatalkan? |
|---|---|
paid_deferred | Ya, kapan saja sebelum mulai diproses setelah 00.00 WIB (hanya jalur ini yang bisa dibatalkan) |
manual_process | Tidak. Order Cek AI dan order jalur manual tidak bisa dibatalkan sendiri |
| Status lain | Tidak. Respons 409 not_cancellable. Hubungi admin untuk refund |
Hanya order yang dibuat lewat API yang bisa dibatalkan di sini. Pembatalan bersifat idempotent: memanggil ulang untuk order yang sudah dibatalkan tidak mengembalikan saldo dua kali.
Respons
{
"ok": true,
"orderId": "TRN-1791697852995-B24A459A",
"status": "refunded",
"refunded": 3000,
"balance": 12000
}
$r = zstore('POST', '/order/cancel?id=' . urlencode($orderId));
$d = json_decode($r['body'], true);
echo $r['status'] === 200 ? 'Saldo kembali: ' . $d['refunded'] : 'Gagal: ' . $d['error'];
const res = await zstore('POST', `/order/cancel?id=${orderId}`);
const data = await res.json();
console.log(res.status === 200 ? `Saldo kembali: ${data.refunded}` : `Gagal: ${data.error}`);
r = zstore("POST", "/order/cancel", params={"id": order_id})
d = r.json()
print("Saldo kembali:", d["refunded"] if r.status_code == 200 else "Gagal: " + d["error"])
Contoh alur lengkap
Kirim PDF, tunggu sampai selesai, lalu simpan hasilnya. Cek status tiap 15 detik. Cek Plagiasi biasanya selesai dalam 5 sampai 20 menit, Cek AI diproses manual sehingga bisa lebih lama.
<?php
// Gunakan fungsi zstore() dari bagian "Klien siap pakai"
$pdf = file_get_contents('skripsi.pdf');
$idem = 'skripsi-' . md5($pdf); // satu key per file
$r = zstore('POST', '/order?filename=skripsi.pdf', $pdf, [
'Content-Type: application/pdf',
'Idempotency-Key: ' . $idem,
]);
$data = json_decode($r['body'], true);
if (!in_array($r['status'], [200, 201], true)) {
exit('Gagal membuat order: ' . $data['error'] . PHP_EOL);
}
$orderId = $data['orderId'];
echo "Order dibuat: $orderId\n";
// Polling
while (true) {
sleep(15);
$order = json_decode(zstore('GET', '/order?id=' . urlencode($orderId))['body'], true)['order'];
echo "Status: {$order['status']}\n";
if ($order['resultReady']) {
$dl = zstore('GET', '/order/download?id=' . urlencode($orderId));
file_put_contents("hasil-$orderId.pdf", $dl['body']);
echo "Hasil disimpan.\n";
break;
}
if (in_array($order['status'], ['error', 'cancelled'], true)) {
exit("Order berhenti: {$order['statusMsg']}\n");
}
}
import fs from 'node:fs';
import crypto from 'node:crypto';
// Gunakan fungsi zstore() dari bagian "Klien siap pakai"
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const pdf = fs.readFileSync('skripsi.pdf');
const idem = 'skripsi-' + crypto.createHash('md5').update(pdf).digest('hex'); // satu key per file
const created = await zstore('POST', '/order?filename=skripsi.pdf', {
body: pdf,
headers: { 'Content-Type': 'application/pdf', 'Idempotency-Key': idem },
});
const data = await created.json();
if (![200, 201].includes(created.status)) {
throw new Error('Gagal membuat order: ' + data.error);
}
const orderId = data.orderId;
console.log('Order dibuat:', orderId);
// Polling
while (true) {
await sleep(15000);
const { order } = await (await zstore('GET', `/order?id=${orderId}`)).json();
console.log('Status:', order.status);
if (order.resultReady) {
const dl = await zstore('GET', `/order/download?id=${orderId}`);
fs.writeFileSync(`hasil-${orderId}.pdf`, Buffer.from(await dl.arrayBuffer()));
console.log('Hasil disimpan.');
break;
}
if (['error', 'cancelled'].includes(order.status)) {
throw new Error('Order berhenti: ' + order.statusMsg);
}
}
import hashlib
import time
# Gunakan fungsi zstore() dari bagian "Klien siap pakai"
with open("skripsi.pdf", "rb") as f:
pdf = f.read()
idem = "skripsi-" + hashlib.md5(pdf).hexdigest() # satu key per file
r = zstore(
"POST", "/order",
params={"filename": "skripsi.pdf"},
data=pdf,
headers={"Content-Type": "application/pdf", "Idempotency-Key": idem},
)
data = r.json()
if r.status_code not in (200, 201):
raise SystemExit("Gagal membuat order: " + data["error"])
order_id = data["orderId"]
print("Order dibuat:", order_id)
# Polling
while True:
time.sleep(15)
order = zstore("GET", "/order", params={"id": order_id}).json()["order"]
print("Status:", order["status"])
if order["resultReady"]:
dl = zstore("GET", "/order/download", params={"id": order_id})
with open(f"hasil-{order_id}.pdf", "wb") as f:
f.write(dl.content)
print("Hasil disimpan.")
break
if order["status"] in ("error", "cancelled"):
raise SystemExit("Order berhenti: " + order["statusMsg"])
Status order
| Status | Arti | Final |
|---|---|---|
paid | Dibayar, menunggu giliran diproses | Tidak |
processing | Sedang diproses | Tidak |
paid_deferred | Dibayar, akan diproses setelah 00.00 WIB (kuota harian penuh) | Tidak |
manual_process | Menunggu diproses tim Z STORE (Cek AI selalu lewat jalur ini) | Tidak |
need_review | Sedang diperiksa tim Z STORE | Tidak |
done | Selesai. Unduh saat resultReady bernilai true | Ya |
error | Gagal diproses. Saldo dikembalikan otomatis jika kegagalan dari sistem | Ya |
cancelled | Dibatalkan | Ya |
refunded | Dibatalkan dan saldo dikembalikan (lewat /order/cancel atau oleh admin) | Ya |
Kode error
Semua error memakai status HTTP yang sesuai dan body JSON berikut. Kolom message tidak selalu ada, jadikan error sebagai acuan di kodemu.
{
"ok": false,
"error": "insufficient_balance",
"balance": 1500,
"price": 3000
}
| HTTP | error | Arti dan tindakan |
|---|---|---|
| 400 | invalid_product | Nilai product bukan turnitin atau turnitin-ai |
| 400 | idempotency_key_required | Header Idempotency-Key kosong atau formatnya salah |
| 400 | file_empty / file_must_be_pdf | Body kosong, atau bukan file PDF |
| 400 | too_many_words | Cek AI: dokumen melebihi batas kata. Lihat maxWords di respons |
| 401 | invalid_api_key | Key salah atau sudah dicabut |
| 402 | insufficient_balance | Saldo kurang. Respons berisi balance dan price. Top up di dashboard |
| 403 | ip_not_allowed | IP server tidak ada di allowlist key |
| 403 | addon_expired | Add on Akses API Key berakhir. Perpanjang di dashboard |
| 403 | account_suspended | Akun ditangguhkan. Hubungi admin |
| 403 | order_closed | Order sedang ditutup sementara |
| 404 | order_not_found | ID order tidak ada atau bukan milik akunmu |
| 404 | product_not_found | Produk sedang tidak tersedia |
| 409 | idempotency_key_conflict | Key sama dipakai untuk file atau produk lain. Pakai key baru |
| 409 | not_cancellable | Order sudah diproses atau lewat batas pembatalan. Hubungi admin untuk refund |
| 409 | not_ready | Hasil belum siap diunduh. Cek status lalu coba lagi |
| 413 | file_too_large | File lebih dari 4 MB |
| 415 | content_type_must_be_application_pdf | Header Content-Type harus application/pdf |
| 429 | rate_limited / too_many_requests | Terlalu banyak request. Tunggu sesuai header Retry-After |
| 429 | daily_order_limit | Batas 100 order API per 24 jam tercapai. Coba lagi besok |
| 429 | too_many_active_orders | Sudah ada 10 order API aktif. Tunggu salah satu selesai |
| 503 | maintenance | Produk sedang maintenance. Coba lagi nanti |
| 503 | slot_full | Kuota harian Cek Plagiasi penuh. Kirim ulang dengan allow_deferred=true |
| 500 | internal_server_error | Kesalahan di sisi server. Saldo tidak terpotong. Coba lagi dengan Idempotency-Key yang sama |
Praktik terbaik
- Satu Idempotency-Key per file. Jika koneksi putus saat mengirim, kirim ulang dengan key yang sama. Saldo tidak terpotong dua kali. Menurunkan key dari hash isi file adalah cara paling aman.
- Polling secukupnya. Cukup cek status tiap 15 sampai 30 detik. Batas 30 request per menit per key dipakai bersama semua endpoint.
- Hentikan polling di status final (
done,error,cancelled,refunded). - Cek saldo sebelum mengirim. Bandingkan
/balancedengan harga dari/pricesupaya tidak terkena402. - Jaga key. Simpan di environment variable atau secret manager. Jika bocor, cabut di halaman API key lalu buat yang baru.
- Salah kirim? Hanya order
paid_deferredyang bisa dibatalkan lewat/order/cancel. Pastikan file sudah benar sebelum mengirim. - Pakai allowlist IP bila server kamu punya IP tetap.