· Nacho Coll · Tutorials · 10 min de lecture
IPFS Upload API — Tutoriel développeur complet
Apprends à uploader des fichiers sur IPFS via REST API. Exemples de code complets en JavaScript, Node.js et curl. Upload JSON, images, utilise des tokens signés pour les uploads côté client.

IPFS Upload API — Tutoriel développeur complet
Uploader des fichiers sur IPFS devrait être aussi simple que faire une requête POST. Dans ce tutoriel tu feras exactement cela — uploader des documents JSON, images et PDF sur IPFS avec rien de plus que fetch() et une clé API. À la fin tu auras un script Node.js complet qui upload du contenu, liste les fichiers, récupère les métadonnées et génère des tokens signés pour des uploads sécurisés côté client. Nouveau sur IPFS ? Commence par Qu’est-ce qu’IPFS Pinning ? pour comprendre les fondamentaux avant de plonger dans le code.

Ce que tu vas construire
- Uploader un objet JSON sur IPFS et obtenir un identifiant de contenu (CID).
- Uploader une image binaire depuis le disque.
- Attacher des descriptions et métadonnées personnalisées aux uploads.
- Interroger ton historique d’uploads par plage de dates.
- Récupérer les détails d’un fichier spécifique.
- Créer des tokens signés à durée limitée pour qu’un navigateur puisse uploader directement sans exposer ta clé API.
- Gérer les erreurs et implémenter une logique de retry.
Tout s’exécute contre une seule URL de base : https://api.ipfs.ninja
1. Setup — Inscription et obtention d’une clé API
- Inscris-toi pour un compte gratuit (pas de carte de crédit requise).
- Va dans API Keys dans la barre latérale du dashboard.
- Clique sur Create key, donne-lui un nom et copie la clé immédiatement — elle ne sera plus affichée.

Ta clé ressemble à bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Chaque exemple ci-dessous l’envoie dans l’en-tête X-Api-Key.
2. Uploader du JSON sur IPFS
L’upload le plus simple : passe un objet JavaScript brut comme content et l’API le sérialise, le pin sur IPFS et retourne le 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);Lance avec node upload-json.mjs. Une réponse réussie ressemble à ceci :
{
"cid": "bafkreigx7gq...",
"sizeMB": 0.0004,
"uris": {
"ipfs": "ipfs://bafkreigx7gq...",
"url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
}
}Le champ url pointe vers une passerelle HTTP publique, donc le contenu est immédiatement accessible dans n’importe quel navigateur.
3. Uploader une image
Les fichiers binaires (images, PDF, audio) sont envoyés sous forme de chaînes encodées en base64 dans le champ 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);L’API détecte le type MIME automatiquement — PNG, JPEG, WebP, GIF et PDF sont tous supportés. Pas d’en-têtes supplémentaires ni de remplacements de content-type requis.
Avec curl, la même opération ressemble à ceci :
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 avec métadonnées
Chaque upload accepte deux champs optionnels : description (label de texte libre) et metadata (paires clé-valeur arbitraires). Les deux sont stockés à côté du CID et retournés lorsque tu listes ou récupères le fichier plus tard.
// 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);Les métadonnées facilitent le filtrage et l’organisation des fichiers de ton côté sans parser le contenu IPFS lui-même.
5. Pinner un CID existant
Si tu as déjà du contenu sur IPFS et veux t’assurer qu’il reste disponible, pin-le par 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. Lister tes fichiers
Récupère chaque fichier que tu as uploadé dans une fenêtre de temps. Les paramètres de requête from et to sont des timestamps Unix en millisecondes.
// 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)"}`);
}Avec 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. Obtenir les détails du fichier
Récupère l’enregistrement complet pour un seul CID, incluant métadonnées, taille et 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. Uploads côté client avec tokens signés
Intégrer une clé API dans un bundle navigateur est un risque de sécurité. À la place, génère un token signé de courte durée sur ton serveur et passe-le au client.
Serveur (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"));Client navigateur
<!-- 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>Le navigateur ne voit jamais ta clé API. Le token signé expire automatiquement. Les tokens sont multi-usages — ils peuvent être utilisés plusieurs fois jusqu’à ce qu’ils expirent ou soient révoqués. Le useCount est suivi mais pas appliqué comme limite.
9. Gestion des erreurs
L’API utilise des codes de statut HTTP conventionnels. Voici ceux que tu devrais gérer explicitement :
| Status | Signification | Que faire |
|---|---|---|
| 400 | Bad request — champs manquants ou invalides | Vérifie que content est présent et valide |
| 401 | Unauthorized — clé API mauvaise ou manquante | Vérifie l’en-tête X-Api-Key |
| 413 | Payload trop volumineux | Réduis la taille du fichier ou divise le contenu |
| 429 | Rate limited | Recule et réessaie avec délai exponentiel |
| 500 | Erreur serveur | Réessaie après un court délai |
Une fonction d’upload résiliente avec backoff exponentiel :
// 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. Exemple complet fonctionnel
Un seul script qui exerce chaque endpoint couvert dans ce tutoriel. Sauvegarde-le comme ipfs-demo.mjs et lance avec 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.");Remplace bws_your_key_here par une vraie clé et lance le script. Chaque étape affiche son résultat pour que tu puisses suivre.
11. Prochaines étapes
Tu sais maintenant comment uploader, pinner, lister et récupérer des fichiers via l’IPFS Upload API et comment garder les uploads navigateur sécurisés avec des tokens signés. Pour une vue plus large incluant l’UI dashboard et des exemples Python, voir Comment uploader des fichiers sur IPFS. Voici où aller à partir d’ici :
- Référence API — Documentation complète des endpoints sur ipfs.ninja/docs.
- Passerelles personnalisées — Sers du contenu IPFS depuis ton propre domaine. Voir le guide de setup gateway.
- Analytics — Suis le volume d’upload, la bande passante et les compteurs de pin dans le dashboard.
- Client HTTP — Pas de SDK requis. Tu peux utiliser le
fetch()standard ou n’importe quel client HTTP pour interagir avec l’API.
Si tu rencontres des problèmes, ouvre un ticket sur support.ipfs.ninja ou rejoins la communauté sur Discord.
