With Pinata, there are a few ways you can pin files to IPFS!

API & SDKs

If you’re a developer that needs to build decentralized applications, then you will likely want to use the Pinata API or Pinata SDK. These make it simple to upload files or raw JSON to IPFS!

Using the SDK

The Pinata Node SDK is designed to work in a backend environment, so if you are trying to upload in a front end client, please observe the API example.

const fs = require('fs');
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK({ pinataJWTKey: 'yourPinataJWTKey'});

const stream = fs.createReadStream('./yourfile.png');
const res = await pinata.pinFileToIPFS(stream)`

The SDK is also equipped to handle folders as well as files, so you can pass a directory into the file path to upload it.

const fs = require('fs');
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK({ pinataJWTKey: 'yourPinataJWTKey'});

const stream = fs.createReadStream('./some-directory');
const res = await pinata.pinFileToIPFS(stream)`

Using the API

The API is a bit more flexible, as it can be used in multiple languages or environments, and is sometimes preferred for client side uploads. With that, we should express warning in that client side applications will expose API keys.

Using your API Keys in a client side application will expose them!!

Here is an example of how you could upload with the API:

const JWT = "YOUR_PINATA_JWT";
const fs = require("fs");

async function pinFileToIPFS() {
  try {
    const formData = new FormData();
    const src = "path/to/file.png";

    const file = fs.createReadStream(src);
    formData.append("file", file);

    const pinataMetadata = JSON.stringify({
      name: "File name",
    });
    formData.append("pinataMetadata", pinataMetadata);

    const pinataOptions = JSON.stringify({
      cidVersion: 0,
    });
    formData.append("pinataOptions", pinataOptions);

    const res = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
      method: "POST",
      headers: {
        Authorization: `Bearer YOUR_PINATA_JWT`,
      },
      body: formData,
    });
    const resData = await res.json();
    console.log(resData);
  } catch (error) {
    console.log(error);
  }
}

The API will expect a File, Blob, or a readableStream for any files uploaded. Here is an example of turning raw data into a Blob and then uploading it.

Pin Blob
const JWT = 'YOUR_PINATA_JWT';

async function pinFileToIPFS() {
  try {
    const text = "Hello World!";
    const blob = new Blob([text], { type: "text/plain" });
    const data = new FormData();
    data.append("file", blob);

    const res = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS",
      method: "POST",
      headers: {
        Authorization: `Bearer ${JWT}`,
      },
      body: data,
    );
    const resData = await res.json();
    console.log(resData);
  } catch (error) {
    console.log(error);
  }
};

Another example of this in action is turning a URL stream into an arrayBuffer, which then gets passed into a new Blob that can then be uploaded to Pinata.

Upload by URL
const JWT = "YOUR_PINATA_JWT";

export default async function uploadByURL(url) {
  try {
    const urlStream = await fetch(url);
    const arrayBuffer = await urlStream.arrayBuffer();
    const blob = new Blob([arrayBuffer]);

    const data = new FormData();
    data.append("file", blob);
    const upload = await fetch(
      "https://api.pinata.cloud/pinning/pinFileToIPFS",
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${JWT}`,
        },
        body: data,
      }
    );
    const uploadRes = await upload.json();
    console.log(uploadRes);
  } catch (error) {
    console.log(error);
  }
}

uploadByURL("https://pocketcast.cloud/og.png");

Folders

Folders can also be uploaded via the API by creating an array of files and mapping over them to add them to the form data. This is different then having a single file entry and having multiple files for that one entry, which does not work.

import fs from "fs"
import FormData from "form-data"
import rfs from "recursive-fs"
import basePathConverter from "base-path-converter"
import got from 'got'

const pinDirectoryToPinata = async () => {
  const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
  const src = "PATH_TO_FOLDER";
  var status = 0;
  try {

    const { dirs, files } = await rfs.read(src);

    let data = new FormData();

    for (const file of files) {
      data.append(`file`, fs.createReadStream(file), {
        filepath: basePathConverter(src, file),
      });
    }    
    
    const response = await got(url, {
      method: 'POST',
      headers: {
        "Authorization": "Bearer PINATA_API_JWT"
      },
      body: data
  })		
		.on('uploadProgress', progress => {
			console.log(progress);
		});

	console.log(JSON.parse(response.body));
  } catch (error) {
    console.log(error);
  }
};

pinDirectoryToPinata()

We also have other tools like the Pinata CLI or Next.js Starter which can be used to upload using API Keys!

Web App

If you’re non-technical, you can use Pinata App to upload files, perfect if you just want to get started with NFTs and IPFS! It’s as simple as clicking the “Upload” button in the top right corner of the files page. Select your file, give it a name, then upload. Once it’s complete you’ll see it listed in the files page.

Start uploading by signing up for a free account!

Pin by CID

Another way you can upload content to Pinata is by transferring content that is already on IPFS. This could be CIDs that are on your own local IPFS node or another IPFS pinning service! You can do this with the “Pin by CID” button in the web app, like so:

Or you can pin by CID with our API using the Pin By CID endpoint:

Pin by CID
const JWT = 'YOUR_PINATA_JWT';

async function pinByCID() {
  try {
    const data = JSON.stringify({
      hashToPin: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4'
    })
    const res = await fetch("https://api.pinata.cloud/pinning/pinByHash",
      method: "POST",
      headers: {
        contentType: 'application/json',
        Authorization: `Bearer ${JWT}`,
      },
      body: data,
    );
    const resData = await res.json();
    console.log(resData);
  } catch (error) {
    console.log(error);
  }
};

This will result in a request_id and Pinata will start looking for the file. Progress can be checked by using the list pinning jobs endpoint.

const JWT = 'YOUR_PINATA_JWT';

async function pinByCID() {
  try {
    const data = JSON.stringify({
      hashToPin: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4'
    })
    const res = await fetch("https://api.pinata.cloud/pinning/pinJobs",
      method: "GET",
      headers: {
        contentType: 'application/json',
        Authorization: `Bearer ${JWT}`,
      },
      body: data,
    );
    const resData = await res.json();
    console.log(resData);
  } catch (error) {
    console.log(error);
  }
};

All possible filters are included in the API reference below, but these are the possible “status” filters:

status
string

Filter by the status of the job in the pinning queue (see potential statuses below) - prechecking Pinata is running preliminary validations on your pin request. - searching Pinata is actively searching for your content on the IPFS network. This may take some time if your content is isolated. - retrieving Pinata has located your content and is now in the process of retrieving it. - expired Pinata wasn’t able to find your content after a day of searching the IPFS network. Please make sure your content is hosted on the IPFS network before trying to pin again. - over_free_limit Pinning this object would put you over the free tier limit. Please add a credit card to continue pinning content. - over_max_size This object is too large of an item to pin. If you’re seeing this, please contact us for a more custom solution. - invalid_object The object you’re attempting to pin isn’t readable by IPFS nodes. Please contact us if you receive this, as we’d like to better understand what you’re attempting to pin. - bad_host_node You provided a host node that was either invalid or unreachable. Please make sure all provided host nodes are online and reachable.

Predetermining the CID

If you find yourself in a position where you want to pre-determine the CID before uploading you can use the multiformats library. The key is making sure you use raw codec and use blobs just like the Pinata API accepts to get the same results.

import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { sha256 } from 'multiformats/hashes/sha2'
const JWT = "YOUR_PINATA_JWT"

async function main(){
  try {
    const text = "Hello World!";
    const blob = new Blob([text], { type: "text/plain" });
    const unit8array = new Uint8Array(await blob.arrayBuffer());
    const bytes = raw.encode(unit8array)
    const hash = await sha256.digest(bytes)
    const cid = CID.create(1, raw.code, hash)
     console.log(cid.toString())
  } catch(error) {
    console.log(error)
  }
}

async function pinFileToIPFS() {
  try {
    const text = "Hello World!";
    const blob = new Blob([text], { type: "text/plain" });
    const data = new FormData();
    data.append("file", blob);
    const options = JSON.stringify({
      cidVersion: 1
    })
    data.append("pinataOptions", options)

    const res = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${JWT}`,
      },
      body: data
    });
    const resData = await res.json();
    console.log(resData.IpfsHash);
  } catch (error) {
    console.log(error);
  }
};

main()
pinFileToIPFS()