· Nacho Coll · Tutorials · 9 хв читання
IPFS Upload API — Повний посібник для розробників
Навчіться завантажувати файли в IPFS через REST API. Повні приклади коду на JavaScript, Node.js та curl. Завантажуйте JSON, зображення, використовуйте підписані токени для клієнтських завантажень.

IPFS Upload API — Повний посібник для розробників
Завантаження файлів у IPFS має бути таким же простим, як надсилання POST-запиту. У цьому посібнику ви зробите саме це — завантажите JSON-документи, зображення та PDF в IPFS, не маючи нічого, крім fetch() та API-ключа. Наприкінці у вас буде повний Node.js скрипт, який завантажує контент, виводить список файлів, отримує метадані та генерує підписані токени для безпечних клієнтських завантажень. Новачок у IPFS? Почніть з Що таке IPFS Pinning?, щоб зрозуміти основи, перш ніж занурюватися в код.

Що ви побудуєте
- Завантажите JSON-об’єкт в IPFS та отримаєте назад ідентифікатор контенту (CID).
- Завантажите бінарне зображення з диска.
- Долучите описи та власні метадані до завантажень.
- Запитаєте історію завантажень за діапазоном дат.
- Отримаєте деталі для конкретного файлу.
- Створите підписані токени з обмеженим часом дії, щоб браузер міг завантажувати безпосередньо, не розкриваючи ваш API-ключ.
- Обробите помилки та реалізуєте логіку повторних спроб.
Усе працює проти однієї базової URL: https://api.ipfs.ninja
1. Налаштування — Реєстрація та отримання API-ключа
- Зареєструйтеся для безкоштовного облікового запису (кредитна картка не потрібна).
- Перейдіть до API Keys на бічній панелі панелі керування.
- Натисніть Create key, дайте йому назву та одразу скопіюйте ключ — він не буде показаний знову.

Ваш ключ виглядає як bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Кожен наведений нижче приклад надсилає його в заголовку X-Api-Key.
2. Завантаження JSON в IPFS
Найпростіше завантаження: передайте звичайний JavaScript-об’єкт як content, і API серіалізує його, закріпить в IPFS та поверне 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);Запустіть за допомогою node upload-json.mjs. Успішна відповідь виглядає так:
{
"cid": "bafkreigx7gq...",
"sizeMB": 0.0004,
"uris": {
"ipfs": "ipfs://bafkreigx7gq...",
"url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
}
}Поле url вказує на публічний HTTP-шлюз, тож контент одразу доступний у будь-якому браузері.
3. Завантаження зображення
Бінарні файли (зображення, PDF, аудіо) надсилаються як base64-закодовані рядки в полі 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 автоматично визначає MIME-тип — PNG, JPEG, WebP, GIF та PDF усі підтримуються. Додаткові заголовки чи перевизначення content-type не потрібні.
З curl та сама операція виглядає так:
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. Завантаження з метаданими
Кожне завантаження приймає два необов’язкові поля: description (вільнотекстовий ярлик) та metadata (довільні пари ключ-значення). Обидва зберігаються разом із CID і повертаються при подальшому виведенні списку чи отриманні файлу.
// 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);Метадані спрощують фільтрацію та організацію файлів на вашому боці без розбору самого контенту IPFS.
5. Закріплення наявного CID
Якщо у вас уже є контент в IPFS і ви хочете гарантувати його доступність, закріпіть його за 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. Список ваших файлів
Отримайте кожен файл, який ви завантажили в межах часового вікна. Параметри запиту from і to — це Unix-таймстампи в мілісекундах.
// 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)"}`);
}З 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. Деталі файлу
Отримайте повний запис для одного CID, включаючи метадані, розмір та таймстампи:
// 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. Клієнтські завантаження з підписаними токенами
Вбудовування API-ключа в браузерний бандл — це ризик безпеки. Натомість згенеруйте короткостроковий підписаний токен на вашому сервері та передайте його клієнту.
Сервер (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"));Клієнт у браузері
<!-- 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>Браузер ніколи не бачить ваш API-ключ. Підписаний токен закінчує дію автоматично. Токени багаторазові — їх можна використовувати кілька разів, доки вони не закінчаться або не будуть відкликані. useCount відстежується, але не застосовується як обмеження.
9. Обробка помилок
API використовує загальноприйняті коди стану HTTP. Ось ті, які слід обробляти явно:
| Статус | Значення | Що робити |
|---|---|---|
| 400 | Bad request — відсутні або недійсні поля | Перевірте, що content присутнє та коректне |
| 401 | Unauthorized — поганий або відсутній API-ключ | Перевірте заголовок X-Api-Key |
| 413 | Payload too large | Зменшіть розмір файлу або розділіть контент |
| 429 | Rate limited | Зачекайте та повторіть з експоненційною затримкою |
| 500 | Server error | Повторіть після короткої затримки |
Стійка функція завантаження з експоненційним 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. Повний робочий приклад
Один скрипт, який використовує кожен endpoint, описаний у цьому посібнику. Збережіть його як ipfs-demo.mjs та запустіть за допомогою 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.");Замініть bws_your_key_here справжнім ключем і запустіть скрипт. Кожен крок виводить свій результат, щоб ви могли стежити за виконанням.
11. Наступні кроки
Тепер ви знаєте, як завантажувати, закріплювати, виводити список та отримувати файли через IPFS Upload API, а також як забезпечити безпеку браузерних завантажень за допомогою підписаних токенів. Для ширшого огляду, що включає UI панелі керування та приклади Python, див. Як завантажувати файли в IPFS. Куди рухатися далі:
- API Reference — Повна документація endpoint на ipfs.ninja/docs.
- Власні шлюзи — Подавайте контент IPFS зі свого домену. Див. посібник з налаштування шлюзу.
- Аналітика — Відстежуйте обсяг завантажень, пропускну здатність та кількість закріплень на панелі керування.
- HTTP-клієнт — SDK не потрібен. Ви можете використовувати стандартний
fetch()або будь-якого HTTP-клієнта для взаємодії з API.
Якщо у вас виникнуть проблеми, відкрийте тікет на support.ipfs.ninja або приєднайтеся до спільноти в Discord.
