· Nacho Coll · Tutorials · 9 min leestijd
IPFS Upload API — Complete ontwikkelaarstutorial
Leer bestanden naar IPFS te uploaden via REST API. Volledige codevoorbeelden in JavaScript, Node.js en curl. Upload JSON, afbeeldingen, gebruik ondertekende tokens voor client-side uploads.

IPFS Upload API — Complete ontwikkelaarstutorial
Bestanden uploaden naar IPFS zou zo eenvoudig moeten zijn als een POST-verzoek doen. In deze tutorial doe je precies dat — JSON-documenten, afbeeldingen en PDF’s uploaden naar IPFS met niet meer dan fetch() en een API-sleutel. Aan het einde heb je een compleet Node.js-script dat content uploadt, bestanden lijst, metadata ophaalt en ondertekende tokens genereert voor veilige client-side uploads. Nieuw bij IPFS? Begin met Wat is IPFS Pinning? om de fundamenten te begrijpen voordat je in de code duikt.

Wat je gaat bouwen
- Een JSON-object uploaden naar IPFS en een content identifier (CID) terugkrijgen.
- Een binaire afbeelding uploaden vanaf de schijf.
- Beschrijvingen en aangepaste metadata aan uploads koppelen.
- Je uploadgeschiedenis bevragen op datumbereik.
- Details ophalen voor een specifiek bestand.
- Tijdgelimiteerde ondertekende tokens maken zodat een browser direct kan uploaden zonder je API-sleutel bloot te stellen.
- Fouten afhandelen en retry-logica implementeren.
Alles draait tegen één basis-URL: https://api.ipfs.ninja
1. Setup — Registreer en krijg een API-sleutel
- Registreer voor een gratis account (geen creditcard vereist).
- Ga naar API Keys in de dashboard-zijbalk.
- Klik op Create key, geef het een naam en kopieer de sleutel onmiddellijk — hij wordt niet opnieuw getoond.

Je sleutel ziet eruit als bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Elk onderstaand voorbeeld verzendt het in de X-Api-Key-header.
2. JSON uploaden naar IPFS
De eenvoudigste upload: geef een gewoon JavaScript-object door als content en de API serialiseert het, pint het op IPFS en retourneert de 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);Voer uit met node upload-json.mjs. Een succesvolle reactie ziet er zo uit:
{
"cid": "bafkreigx7gq...",
"sizeMB": 0.0004,
"uris": {
"ipfs": "ipfs://bafkreigx7gq...",
"url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
}
}Het url-veld wijst naar een publieke HTTP-gateway, dus de content is onmiddellijk toegankelijk in elke browser.
3. Een afbeelding uploaden
Binaire bestanden (afbeeldingen, PDF’s, audio) worden verzonden als base64-gecodeerde strings in het content-veld.
// 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);De API detecteert het MIME-type automatisch — PNG, JPEG, WebP, GIF en PDF worden allemaal ondersteund. Geen extra headers of content-type overrides vereist.
Met curl ziet dezelfde operatie er zo uit:
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. Upload met metadata
Elke upload accepteert twee optionele velden: description (vrij tekstlabel) en metadata (willekeurige sleutel-waarde paren). Beide worden naast de CID opgeslagen en geretourneerd wanneer je het bestand later lijst of ophaalt.
// 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 maakt het gemakkelijk om bestanden aan jouw kant te filteren en te organiseren zonder de IPFS-content zelf te parsen.
5. Een bestaande CID pinnen
Als je al content op IPFS hebt en wilt verzekeren dat deze beschikbaar blijft, pin het dan op 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. Lijst je bestanden
Haal elk bestand op dat je hebt geüpload binnen een tijdvenster. De query-parameters from en to zijn Unix-timestamps in milliseconden.
// 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)"}`);
}Met 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. Bestandsdetails ophalen
Haal de volledige record op voor een enkele CID, inclusief metadata, grootte en timestamps:
// 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. Client-side uploads met ondertekende tokens
Een API-sleutel insluiten in een browserbundel is een veiligheidsrisico. Genereer in plaats daarvan een kortlevend ondertekend token op je server en geef het door aan de client.
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"));Browserclient
<!-- 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>De browser ziet je API-sleutel nooit. Het ondertekende token verloopt automatisch. Tokens zijn multi-use — ze kunnen meerdere keren worden gebruikt totdat ze verlopen of worden ingetrokken. De useCount wordt bijgehouden maar niet afgedwongen als een limiet.
9. Foutafhandeling
De API gebruikt conventionele HTTP-statuscodes. Hier zijn degene die je expliciet zou moeten afhandelen:
| Status | Betekenis | Wat te doen |
|---|---|---|
| 400 | Bad request — ontbrekende of ongeldige velden | Controleer of content aanwezig en geldig is |
| 401 | Unauthorized — slechte of ontbrekende API-sleutel | Verifieer de X-Api-Key-header |
| 413 | Payload te groot | Verminder bestandsgrootte of splits content |
| 429 | Rate limited | Terugtrekken en opnieuw proberen met exponentiële vertraging |
| 500 | Serverfout | Opnieuw proberen na een korte vertraging |
Een veerkrachtige uploadfunctie met exponentiële backoff:
// 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. Volledig werkend voorbeeld
Een enkel script dat elk eindpunt in deze tutorial gebruikt. Sla het op als ipfs-demo.mjs en voer uit met 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.");Vervang bws_your_key_here door een echte sleutel en voer het script uit. Elke stap drukt zijn resultaat af zodat je kunt volgen.
11. Volgende stappen
Je weet nu hoe je bestanden kunt uploaden, pinnen, lijsten en ophalen via de IPFS Upload API en hoe je browseruploads veilig kunt houden met ondertekende tokens. Voor een breder overzicht inclusief de dashboard-UI en Python-voorbeelden, zie Hoe bestanden te uploaden naar IPFS. Hier is waar je vanaf hier kunt gaan:
- API-referentie — Volledige eindpuntdocumentatie op ipfs.ninja/docs.
- Aangepaste gateways — Serveer IPFS-content vanaf je eigen domein. Zie de gateway-setupgids.
- Analytics — Volg uploadvolume, bandbreedte en pinaantallen in het dashboard.
- HTTP-client — Geen SDK vereist. Je kunt standaard
fetch()of een andere HTTP-client gebruiken om met de API te communiceren.
Als je problemen tegenkomt, open dan een ticket op support.ipfs.ninja of sluit je aan bij de community op Discord.
