· Nacho Coll · Tutorials · 9 menit baca
IPFS Upload API — Tutorial Pengembang Lengkap
Pelajari cara mengunggah file ke IPFS melalui REST API. Contoh kode lengkap di JavaScript, Node.js, dan curl. Unggah JSON, gambar, gunakan token yang ditandatangani untuk unggahan sisi klien.

IPFS Upload API — Tutorial Pengembang Lengkap
Mengunggah file ke IPFS seharusnya semudah membuat permintaan POST. Dalam tutorial ini Anda akan melakukan hal itu — mengunggah dokumen JSON, gambar, dan PDF ke IPFS hanya dengan fetch() dan kunci API. Di akhir Anda akan memiliki script Node.js lengkap yang mengunggah konten, mencantumkan file, mengambil metadata, dan menghasilkan token yang ditandatangani untuk unggahan sisi klien yang aman. Baru di IPFS? Mulai dengan Apa itu IPFS Pinning? untuk memahami fundamental sebelum terjun ke kode.

Apa yang Akan Anda Bangun
- Mengunggah objek JSON ke IPFS dan mendapatkan kembali pengidentifikasi konten (CID).
- Mengunggah gambar biner dari disk.
- Melampirkan deskripsi dan metadata kustom ke unggahan.
- Mengkueri riwayat unggahan Anda berdasarkan rentang tanggal.
- Mengambil detail untuk file tertentu.
- Membuat token yang ditandatangani dengan waktu terbatas sehingga browser dapat mengunggah langsung tanpa mengekspos kunci API Anda.
- Menangani kesalahan dan menerapkan logika percobaan ulang.
Semuanya berjalan terhadap URL dasar tunggal: https://api.ipfs.ninja
1. Pengaturan — Daftar dan Dapatkan Kunci API
- Daftar untuk akun gratis (tidak perlu kartu kredit).
- Buka API Keys di sidebar dashboard.
- Klik Create key, beri nama, dan segera salin kuncinya — itu tidak akan ditampilkan lagi.

Kunci Anda terlihat seperti bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Setiap contoh di bawah mengirimkannya di header X-Api-Key.
2. Unggah JSON ke IPFS
Unggahan paling sederhana: lewati objek JavaScript biasa sebagai content dan API akan menserialisasinya, mem-pin-nya ke IPFS, dan mengembalikan CID.
// upload-json.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const payload = {
content: {
name: "Galactic Badge #42",
description: "Proof of attendance — Galactic Meetup 2026",
attributes: [
{ trait_type: "Event", value: "Galactic Meetup" },
{ trait_type: "Year", value: 2026 }
]
}
};
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify(payload)
});
const data = await res.json();
console.log("CID :", data.cid);
console.log("Size:", data.sizeMB, "MB");
console.log("IPFS:", data.uris.ipfs);
console.log("URL :", data.uris.url);Jalankan dengan node upload-json.mjs. Respons sukses terlihat seperti ini:
{
"cid": "bafkreigx7gq...",
"sizeMB": 0.0004,
"uris": {
"ipfs": "ipfs://bafkreigx7gq...",
"url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
}
}Bidang url menunjuk ke gateway HTTP publik, sehingga konten segera dapat diakses di browser mana pun.
3. Unggah Gambar
File biner (gambar, PDF, audio) dikirim sebagai string yang dikodekan base64 di bidang content.
// upload-image.mjs
import { readFileSync } from "node:fs";
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const imageBuffer = readFileSync("./photo.png");
const base64 = imageBuffer.toString("base64");
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({ content: base64 })
});
const data = await res.json();
console.log("Image CID:", data.cid);
console.log("Gateway :", data.uris.url);API mendeteksi tipe MIME secara otomatis — PNG, JPEG, WebP, GIF, dan PDF semuanya didukung. Tidak diperlukan header tambahan atau penggantian content-type.
Dengan curl, operasi yang sama terlihat seperti ini:
BASE64=$(base64 -w 0 photo.png)
curl -X POST https://api.ipfs.ninja/upload/new \
-H "Content-Type: application/json" \
-H "X-Api-Key: bws_your_key_here" \
-d "{\"content\": \"$BASE64\"}"4. Unggah dengan Metadata
Setiap unggahan menerima dua bidang opsional: description (label teks bebas) dan metadata (pasangan kunci-nilai arbitrer). Keduanya disimpan bersama dengan CID dan dikembalikan saat Anda mencantumkan atau mengambil file nanti.
// upload-with-metadata.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
content: { title: "Meeting Notes", body: "Q1 roadmap recap..." },
description: "Q1 2026 planning meeting notes",
metadata: {
project: "acme-app",
author: "dana",
version: "1"
}
})
});
const data = await res.json();
console.log("CID:", data.cid);Metadata memudahkan untuk memfilter dan mengorganisir file di sisi Anda tanpa mengurai konten IPFS itu sendiri.
5. Pin CID yang Sudah Ada
Jika Anda sudah memiliki konten di IPFS dan ingin memastikan tetap tersedia, pin dengan CID:
// pin-cid.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const res = await fetch(`${API}/pin`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
cid: "bafkreigx7gq...",
description: "Pinned from external source"
})
});
const data = await res.json();
console.log("Pinned:", data.cid);6. Daftar File Anda
Ambil setiap file yang telah Anda unggah dalam jendela waktu. Parameter kueri from dan to adalah timestamp Unix dalam milidetik.
// list-files.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const now = Date.now();
const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;
const url = `${API}/upload/list?from=${oneWeekAgo}&to=${now}`;
const res = await fetch(url, {
headers: { "X-Api-Key": API_KEY }
});
const files = await res.json();
for (const file of files) {
console.log(`${file.cid} ${file.description ?? "(no description)"}`);
}Dengan curl:
FROM=$(($(date +%s) * 1000 - 604800000))
TO=$(($(date +%s) * 1000))
curl -s "https://api.ipfs.ninja/upload/list?from=$FROM&to=$TO" \
-H "X-Api-Key: bws_your_key_here" | jq .7. Dapatkan Detail File
Ambil catatan lengkap untuk satu CID, termasuk metadata, ukuran, dan timestamp:
// get-file.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const CID = "bafkreigx7gq...";
const res = await fetch(`${API}/file/${CID}`, {
headers: { "X-Api-Key": API_KEY }
});
const details = await res.json();
console.log(JSON.stringify(details, null, 2));8. Unggahan Sisi Klien dengan Token yang Ditandatangani
Menyematkan kunci API ke bundel browser adalah risiko keamanan. Sebagai gantinya, hasilkan token yang ditandatangani berumur pendek di server Anda dan teruskan ke klien.
Server (Express)
// server.mjs
import express from "express";
const app = express();
const API = "https://api.ipfs.ninja";
const API_KEY = process.env.IPFS_API_KEY;
app.post("/api/upload-token", async (req, res) => {
const response = await fetch(`${API}/upload/signed-url`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
name: "browser-upload",
expiresIn: 600 // token valid for 10 minutes
})
});
const { token, tokenId, expiresAt } = await response.json();
res.json({ token, tokenId, expiresAt });
});
app.listen(3000, () => console.log("Server running on :3000"));Klien Browser
<!-- upload.html -->
<input type="file" id="filePicker" />
<button id="uploadBtn">Upload to IPFS</button>
<pre id="result"></pre>
<script>
const API = "https://api.ipfs.ninja";
document.getElementById("uploadBtn").addEventListener("click", async () => {
// 1. Get a signed token from your own backend
const tokenRes = await fetch("/api/upload-token", { method: "POST" });
const { token } = await tokenRes.json();
// 2. Read the selected file as base64
const file = document.getElementById("filePicker").files[0];
if (!file) return alert("Pick a file first");
const base64 = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(",")[1]);
reader.readAsDataURL(file);
});
// 3. Upload directly to IPFS using the signed token
const uploadRes = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Signed ${token}`
},
body: JSON.stringify({
content: base64,
description: file.name
})
});
const data = await uploadRes.json();
document.getElementById("result").textContent = JSON.stringify(data, null, 2);
});
</script>Browser tidak pernah melihat kunci API Anda. Token yang ditandatangani kedaluwarsa secara otomatis. Token bersifat multi-use — dapat digunakan beberapa kali sampai kedaluwarsa atau dicabut. useCount dilacak tetapi tidak diberlakukan sebagai batas.
9. Penanganan Kesalahan
API menggunakan kode status HTTP konvensional. Berikut adalah yang harus Anda tangani secara eksplisit:
| Status | Arti | Apa yang harus dilakukan |
|---|---|---|
| 400 | Bad request — bidang yang hilang atau tidak valid | Periksa apakah content ada dan valid |
| 401 | Unauthorized — kunci API buruk atau hilang | Verifikasi header X-Api-Key |
| 413 | Payload terlalu besar | Kurangi ukuran file atau pisah konten |
| 429 | Dibatasi laju | Mundur dan coba lagi dengan penundaan eksponensial |
| 500 | Kesalahan server | Coba lagi setelah penundaan singkat |
Fungsi unggah yang tangguh dengan backoff eksponensial:
// resilient-upload.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
async function uploadWithRetry(content, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({ content })
});
if (res.ok) return await res.json();
const body = await res.text();
if (res.status === 401) {
throw new Error(`Authentication failed: ${body}`);
}
if (res.status === 413) {
throw new Error(`Payload too large — reduce file size. ${body}`);
}
if (res.status === 429 || res.status >= 500) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.warn(`Attempt ${attempt} failed (${res.status}). Retrying in ${delay}ms...`);
await new Promise((r) => setTimeout(r, delay));
continue;
}
throw new Error(`Upload failed (${res.status}): ${body}`);
}
throw new Error("Upload failed after all retries");
}
// Usage
const data = await uploadWithRetry({ hello: "world" });
console.log("CID:", data.cid);10. Contoh Kerja Lengkap
Sebuah script tunggal yang melatih setiap endpoint yang dibahas dalam tutorial ini. Simpan sebagai ipfs-demo.mjs dan jalankan dengan node ipfs-demo.mjs.
// ipfs-demo.mjs
import { readFileSync } from "node:fs";
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const headers = {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
};
async function request(method, path, body) {
const opts = { method, headers };
if (body) opts.body = JSON.stringify(body);
const res = await fetch(`${API}${path}`, opts);
if (!res.ok) throw new Error(`${method} ${path} → ${res.status}: ${await res.text()}`);
return res.json();
}
// --- 1. Upload JSON ---
console.log("--- Upload JSON ---");
const jsonResult = await request("POST", "/upload/new", {
content: { name: "Demo Token", edition: 1 },
description: "Tutorial demo — JSON upload",
metadata: { tutorial: "true" }
});
console.log("CID:", jsonResult.cid);
console.log("URL:", jsonResult.uris.url);
// --- 2. Upload an image (if photo.png exists) ---
try {
const img = readFileSync("./photo.png");
console.log("\n--- Upload Image ---");
const imgResult = await request("POST", "/upload/new", {
content: img.toString("base64"),
description: "Tutorial demo — image upload"
});
console.log("CID:", imgResult.cid);
console.log("URL:", imgResult.uris.url);
} catch {
console.log("\n--- Skipping image upload (no photo.png found) ---");
}
// --- 3. Pin the JSON CID ---
console.log("\n--- Pin CID ---");
const pinResult = await request("POST", "/pin", {
cid: jsonResult.cid,
description: "Pinned from tutorial"
});
console.log("Pinned:", pinResult.cid);
// --- 4. Get file details ---
console.log("\n--- File Details ---");
const details = await request("GET", `/file/${jsonResult.cid}`);
console.log(JSON.stringify(details, null, 2));
// --- 5. List recent files ---
console.log("\n--- Recent Files ---");
const now = Date.now();
const oneHourAgo = now - 60 * 60 * 1000;
const files = await request("GET", `/upload/list?from=${oneHourAgo}&to=${now}`);
console.log(`Found ${files.length} file(s) in the last hour`);
for (const f of files) {
console.log(` - ${f.cid} ${f.description ?? ""}`);
}
// --- 6. Create a signed upload token ---
console.log("\n--- Signed Token ---");
const tokenResult = await request("POST", "/upload/signed-url", {
name: "demo-token",
expiresIn: 300
});
console.log("Token ID :", tokenResult.tokenId);
console.log("Expires :", new Date(tokenResult.expiresAt).toISOString());
// --- 7. Upload using the signed token ---
console.log("\n--- Upload with Signed Token ---");
const signedRes = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Signed ${tokenResult.token}`
},
body: JSON.stringify({
content: { note: "Uploaded with a signed token" }
})
});
const signedData = await signedRes.json();
console.log("CID:", signedData.cid);
console.log("\nDone.");Ganti bws_your_key_here dengan kunci asli dan jalankan script. Setiap langkah mencetak hasilnya sehingga Anda dapat mengikuti.
11. Langkah Selanjutnya
Anda sekarang tahu cara mengunggah, mem-pin, mencantumkan, dan mengambil file melalui IPFS Upload API dan cara menjaga unggahan browser tetap aman dengan token yang ditandatangani. Untuk gambaran lebih luas termasuk UI dashboard dan contoh Python, lihat Cara Mengunggah File ke IPFS. Berikut ke mana harus pergi dari sini:
- Referensi API — Dokumentasi endpoint lengkap di ipfs.ninja/docs.
- Gateway Kustom — Sajikan konten IPFS dari domain Anda sendiri. Lihat panduan pengaturan gateway.
- Analitik — Lacak volume unggahan, bandwidth, dan jumlah pin di dashboard.
- Klien HTTP — Tidak diperlukan SDK. Anda dapat menggunakan
fetch()standar atau klien HTTP apa pun untuk berinteraksi dengan API.
Jika Anda mengalami masalah, buka tiket di support.ipfs.ninja atau bergabung dengan komunitas di Discord.
