· Nacho Coll · Tutorials  · 10 min de lectura

IPFS Upload API — Tutorial completo para desarrolladores

Aprende a subir ficheros a IPFS mediante REST API. Ejemplos completos de código en JavaScript, Node.js y curl. Sube JSON, imágenes y utiliza tokens firmados para subidas desde el cliente.

Aprende a subir ficheros a IPFS mediante REST API. Ejemplos completos de código en JavaScript, Node.js y curl. Sube JSON, imágenes y utiliza tokens firmados para subidas desde el cliente.

IPFS Upload API — Tutorial completo para desarrolladores

Subir ficheros a IPFS debería ser tan sencillo como hacer una petición POST. En este tutorial harás justamente eso: subirás documentos JSON, imágenes y PDFs a IPFS con nada más que fetch() y una clave API. Al final tendrás un script completo de Node.js que sube contenido, lista ficheros, recupera metadatos y genera tokens firmados para subidas seguras desde el navegador. ¿Eres nuevo en IPFS? Empieza con ¿Qué es IPFS Pinning? para entender los fundamentos antes de meterte en el código.

IPFS Ninja API keys management page

Lo que vas a construir

  • Subir un objeto JSON a IPFS y obtener un identificador de contenido (CID).
  • Subir una imagen binaria desde el disco.
  • Adjuntar descripciones y metadatos personalizados a las subidas.
  • Consultar tu historial de subidas por rango de fechas.
  • Recuperar los detalles de un fichero concreto.
  • Crear tokens firmados con tiempo limitado para que un navegador pueda subir directamente sin exponer tu clave API.
  • Gestionar errores e implementar lógica de reintentos.

Todo se ejecuta contra una única URL base: https://api.ipfs.ninja

1. Configuración — Regístrate y obtén una clave API

  1. Regístrate para una cuenta gratuita (no se requiere tarjeta de crédito).
  2. Ve a API Keys en la barra lateral del dashboard.
  3. Haz clic en Create key, ponle un nombre y copia la clave inmediatamente: no volverá a mostrarse.
API Keys page — create and manage your keys

Tu clave tiene este aspecto: bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Todos los ejemplos siguientes la envían en la cabecera X-Api-Key.

2. Subir JSON a IPFS

La subida más sencilla: pasa un objeto JavaScript plano como content y la API lo serializa, lo fija en IPFS y devuelve el 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);

Ejecútalo con node upload-json.mjs. Una respuesta correcta tiene esta forma:

{
  "cid": "bafkreigx7gq...",
  "sizeMB": 0.0004,
  "uris": {
    "ipfs": "ipfs://bafkreigx7gq...",
    "url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
  }
}

El campo url apunta a una pasarela HTTP pública, así que el contenido es accesible de inmediato en cualquier navegador.

3. Subir una imagen

Los ficheros binarios (imágenes, PDFs, audio) se envían como cadenas codificadas en base64 en el campo 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);

La API detecta automáticamente el tipo MIME: PNG, JPEG, WebP, GIF y PDF están todos soportados. No se necesitan cabeceras adicionales ni sobreescribir el content-type.

Con curl, la misma operación tiene este aspecto:

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. Subida con metadatos

Cada subida acepta dos campos opcionales: description (etiqueta de texto libre) y metadata (parejas clave-valor arbitrarias). Ambos se guardan junto al CID y se devuelven al listar o recuperar el fichero más adelante.

// 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);

Los metadatos facilitan filtrar y organizar ficheros desde tu lado sin tener que parsear el propio contenido de IPFS.

5. Fijar un CID existente

Si ya tienes contenido en IPFS y quieres asegurarte de que sigue disponible, fíjalo por 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. Listar tus ficheros

Recupera todos los ficheros que has subido dentro de una ventana temporal. Los parámetros de consulta from y to son timestamps Unix en milisegundos.

// 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)"}`);
}

Con 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. Detalles del fichero

Recupera el registro completo de un CID, incluyendo metadatos, tamaño y marcas temporales:

// 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. Subidas desde el cliente con tokens firmados

Incrustar una clave API en un bundle de navegador es un riesgo de seguridad. En su lugar, genera un token firmado de vida corta en tu servidor y pásaselo al cliente.

Servidor (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"));

Cliente en el navegador

<!-- 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>

El navegador nunca ve tu clave API. El token firmado caduca automáticamente. Los tokens son de uso múltiple: pueden utilizarse varias veces hasta que caducan o se revocan. El useCount se registra pero no se aplica como límite.

9. Gestión de errores

La API utiliza códigos de estado HTTP convencionales. Estos son los que deberías gestionar de forma explícita:

EstadoSignificadoQué hacer
400Bad request — campos que faltan o no son válidosComprueba que content está presente y es válido
401Unauthorized — clave API errónea o ausenteVerifica la cabecera X-Api-Key
413Payload too largeReduce el tamaño del fichero o divide el contenido
429Rate limitedHaz back off y reintenta con retardo exponencial
500Server errorReintenta tras una pequeña espera

Una función de subida resistente con backoff exponencial:

// 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. Ejemplo completo y funcional

Un único script que ejerce cada endpoint cubierto en este tutorial. Guárdalo como ipfs-demo.mjs y ejecútalo con 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.");

Sustituye bws_your_key_here por una clave real y ejecuta el script. Cada paso imprime su resultado para que puedas seguirlo.

11. Siguientes pasos

Ahora sabes cómo subir, fijar, listar y recuperar ficheros a través de la IPFS Upload API y cómo mantener seguras las subidas desde el navegador con tokens firmados. Para una visión más amplia que incluye la UI del dashboard y ejemplos en Python, consulta Cómo subir ficheros a IPFS. Por dónde seguir desde aquí:

  • API Reference — Documentación completa de los endpoints en ipfs.ninja/docs.
  • Pasarelas personalizadas — Sirve contenido IPFS desde tu propio dominio. Consulta la guía de configuración de pasarela.
  • Analítica — Sigue el volumen de subidas, el ancho de banda y el número de pins en el dashboard.
  • Cliente HTTP — No se requiere ningún SDK. Puedes usar el fetch() estándar o cualquier cliente HTTP para interactuar con la API.

Si te topas con problemas, abre un ticket en support.ipfs.ninja o únete a la comunidad en Discord.

Volver al Blog

Artículos Relacionados

Ver Todos los Artículos »