· Nacho Coll · Tutorials  · 10 min de lectura

IPFS Upload API — Tutorial completo para desarrolladores

Aprende a subir archivos a IPFS vía REST API. Ejemplos de código completos en JavaScript, Node.js y curl. Sube JSON, imágenes, usa tokens firmados para uploads del lado del cliente.

Aprende a subir archivos a IPFS vía REST API. Ejemplos de código completos en JavaScript, Node.js y curl. Sube JSON, imágenes, usa tokens firmados para uploads del lado del cliente.

IPFS Upload API — Tutorial completo para desarrolladores

Subir archivos a IPFS debería ser tan simple como hacer una petición POST. En este tutorial harás exactamente eso — subir 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 archivos, recupera metadatos y genera tokens firmados para uploads seguros del lado del cliente. ¿Nuevo en IPFS? Empieza con ¿Qué es IPFS Pinning? para entender los fundamentos antes de sumergirte en el código.

IPFS Ninja API keys management page

Lo que construirás

  • 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 los uploads.
  • Consultar tu historial de uploads por rango de fechas.
  • Recuperar detalles de un archivo específico.
  • Crear tokens firmados con tiempo limitado para que un navegador pueda subir directamente sin exponer tu clave API.
  • Manejar errores e implementar lógica de reintento.

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

1. Setup — 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, dale un nombre y copia la clave inmediatamente — no se mostrará de nuevo.
API Keys page — create and manage your keys

Tu clave se ve como bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Cada ejemplo a continuación la envía en el header X-Api-Key.

2. Subir JSON a IPFS

El upload más simple: pasa un objeto JavaScript plano como content y la API lo serializa, lo pinea 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);

Ejecuta con node upload-json.mjs. Una respuesta exitosa se ve así:

{
  "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, por lo que el contenido es inmediatamente accesible en cualquier navegador.

3. Subir una imagen

Los archivos binarios (imágenes, PDFs, audio) se envían como strings codificados 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 el tipo MIME automáticamente — PNG, JPEG, WebP, GIF y PDF son todos soportados. No se requieren headers adicionales ni overrides de content-type.

Con curl la misma operación se ve así:

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

Cada upload acepta dos campos opcionales: description (etiqueta de texto libre) y metadata (pares clave-valor arbitrarios). Ambos se almacenan junto con el CID y se devuelven cuando listas o recuperas el archivo más tarde.

// 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 archivos en tu lado sin parsear el contenido IPFS mismo.

5. Pinear un CID existente

Si ya tienes contenido en IPFS y quieres asegurarte de que se mantenga disponible, pínealo 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 archivos

Recupera cada archivo que has subido dentro de una ventana de tiempo. 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. Obtener detalles del archivo

Recupera el registro completo para un solo CID, incluyendo metadatos, tamaño y 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 del lado del cliente con tokens firmados

Embeber una clave API en un bundle de navegador es un riesgo de seguridad. En su lugar, genera un token firmado de corta duración en tu servidor y pásalo 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 de 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 expira automáticamente. Los tokens son multi-uso — se pueden usar varias veces hasta que expiren o sean revocados. El useCount se rastrea pero no se aplica como límite.

9. Manejo de errores

La API usa códigos de estado HTTP convencionales. Aquí están los que deberías manejar explícitamente:

StatusSignificadoQué hacer
400Bad request — campos faltantes o inválidosVerifica que content esté presente y sea válido
401Unauthorized — clave API mala o faltanteVerifica el header X-Api-Key
413Payload demasiado grandeReduce el tamaño del archivo o divide el contenido
429Rate limitedRetírate y reintenta con delay exponencial
500Error del servidorReintenta tras un delay corto

Una función de upload 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 funcional

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

Reemplaza bws_your_key_here con una clave real y ejecuta el script. Cada paso imprime su resultado para que puedas seguir.

11. Siguientes pasos

Ahora sabes cómo subir, pinear, listar y recuperar archivos a través de la IPFS Upload API y cómo mantener los uploads del navegador seguros con tokens firmados. Para una visión más amplia que incluye la UI del dashboard y ejemplos en Python, mira Cómo subir archivos a IPFS. Aquí es a dónde ir desde aquí:

  • Referencia de API — Documentación completa de endpoints en ipfs.ninja/docs.
  • Gateways personalizados — Sirve contenido IPFS desde tu propio dominio. Mira la guía de setup de gateway.
  • Analytics — Rastrea volumen de upload, ancho de banda y conteos de pin en el dashboard.
  • Cliente HTTP — No se requiere SDK. Puedes usar fetch() estándar o cualquier cliente HTTP para interactuar con la API.

Si te encuentras 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 »