Pin File

Pinning a JSON object? Check out our JSON optimized pinJSONToIPFS endpoint.

Endpoint

/pinning/pinFileToIPFS

Description

This endpoint allows the sender to add and pin any file, or directory, to Pinata's IPFS nodes.

ANY FILE? Yeah! Any file. Here at Pinata, you can even host the plans for that awesome spaceship you're building.

Type

POST

Headers

pinata_api_key: (put your personal pinata api key here)

pinata_secret_api_key: (put your personal pinata secret api key here)

OR

Authorization: Bearer (put your pinata JWT here)

Body

The body for this request needs to take the form of a multipart/form-data body with the following key / values:

file

The first key in the form-data body should be named "file", and the value should be the file you're attempting to upload to Pinata.

pinataOptions (OPTIONAL)

Pinata supports adding additional options when pinning a file. The following options are currently supported for this endpoint:

  • cidVersion - The CID Version IPFS will use when creating a hash for your content. Valid options are: "0" (CIDv0) or "1" (CIDv1)

  • wrapWithDirectory - Wrap your content inside of a directory when adding to IPFS. This allows users to retrieve content via a filename instead of just a hash. For a more detailed explanation, see this informative blogpost. Valid options are: true or false

  • customPinPolicy - a custom pin policy for the piece of content being pinned.

    Providing a custom pin policy as part of a request means that the content being pinned will be replicated differently from the user's default pin policy found under the Account page.

Related to the above, you can read more about pin policies here. To pass in a custom pin policy, pass in a "customPinPolicy" object that takes the following form:

pinataOptions: {
customPinPolicy: {
regions: [
{
id: 'FRA1',
desiredReplicationCount: 1
},
{
id: 'NYC1',
desiredReplicationCount: 2
}
]
}
}

The ids of currently available public regions are:

  • FRA1 - Frankfurt, Germany (max 2 replications)

  • NYC1 - New York City, USA (max 2 replications)

pinataMetadata (OPTIONAL)

In addition to pinning your file to Pinata, you also have the option to include metadata for Pinata to store.

This metadata can later be used for easy querying on what you've pinned with our userPinList request.

The optional metadata object takes the following form:

{
name: A custom name. If you don't provide this value, it will automatically be populated by the original name of the file you've uploaded,
keyvalues: {
customKey: customValue,
customKey2: customValue2
}
}

The key values object allows for users to provide any custom key / value pairs they want for the file being uploaded. These values can be:

  • Strings

  • Numbers (integers or decimals)

  • Dates (Provided in ISO_8601 format)

As of right now, this is limited to 10 key/value pairs, but if this is a problem for you, please let us know!

A Real World JSON Example

As an example, let's pretend you're building a service for lawyers and want to tag every IPFS upload with the Lawyer's ID, the ID for the Lawyer's client, a charge code, and the cost of the service being provided.

Your pinataMetadata object may look like this:

{
name: 'ExampleNameOfDocument.pdf'
keyvalues: {
LawyerName: 'Lawyer001',
ClientID: 'Client002',
ChargeCode: 'Charge003'
Cost: 100.00
}
}

For examples on how to query Pinata's database for pins based on this metadata check out the documentation for our userPinList.

Response

{
IpfsHash: This is the IPFS multi-hash provided back for your content,
PinSize: This is how large (in bytes) the content you just pinned is,
Timestamp: This is the timestamp for your content pinning (represented in ISO 8601 format)
}

Postman Example

JavaScript Example With Axios

In the javascript example below, we pass in our API keys from elsewhere (hopefully in a secure way).

For our file, the below example shows what it would look like if we gathered the file from the api's source files. However, you'll likely gather it from elsewhere.

The example shows primarily shows how we need to set up the header for this call. Note how we're setting up the boundary, which is needed for the Pinata API to accept a file upload.

//imports needed for this function
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
export const pinFileToIPFS = (pinataApiKey, pinataSecretApiKey) => {
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
//we gather a local file for this example, but any valid readStream source will work here.
let data = new FormData();
data.append('file', fs.createReadStream('./yourfile.png'));
//You'll need to make sure that the metadata is in the form of a JSON object that's been convered to a string
//metadata is optional
const metadata = JSON.stringify({
name: 'testname',
keyvalues: {
exampleKey: 'exampleValue'
}
});
data.append('pinataMetadata', metadata);
//pinataOptions are optional
const pinataOptions = JSON.stringify({
cidVersion: 0,
customPinPolicy: {
regions: [
{
id: 'FRA1',
desiredReplicationCount: 1
},
{
id: 'NYC1',
desiredReplicationCount: 2
}
]
}
});
data.append('pinataOptions', pinataOptions);
return axios
.post(url, data, {
maxBodyLength: 'Infinity', //this is needed to prevent axios from erroring out with large files
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey
}
})
.then(function (response) {
//handle response here
})
.catch(function (error) {
//handle error here
});
};

Pinning a directory example

This endpoint also allows users to pin an entire directory to IPFS. This works almost identically to pinning a file, with the main difference being that we provide an array of files and need to provide a relative file path for each file in the directory.

However, our servers will use the exact path that's provided for each file, so it's important that each path begins with the "base" directory that is being uploaded. As an example, if your directory is located at "./../myBuilds/desiredBuild" on your local machine, then each file path should start with "desiredBuild".

So if your directory isn't in the same directory as your upload process, you'll need to modify each path before sending it.

For convenience, our engineering team created an npm package that can help you out with this: base-path-converter.

//imports needed for this function
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
const recursive = require('recursive-fs');
const basePathConverter = require('base-path-converter');
export const pinDirectoryToIPFS = (pinataApiKey, pinataSecretApiKey) => {
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
const src = './exampleDirectory';
//we gather the files from a local directory in this example, but a valid readStream is all that's needed for each file in the directory.
recursive.readdirr(src, function (err, dirs, files) {
let data = new FormData();
files.forEach((file) => {
//for each file stream, we need to include the correct relative file path
data.append(`file`, fs.createReadStream(file), {
filepath: basePathConverter(src, file)
});
});
const metadata = JSON.stringify({
name: 'testname',
keyvalues: {
exampleKey: 'exampleValue'
}
});
data.append('pinataMetadata', metadata);
return axios
.post(url, data, {
maxBodyLength: 'Infinity', //this is needed to prevent axios from erroring out with large directories
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey
}
})
.then(function (response) {
//handle response here
})
.catch(function (error) {
//handle error here
});
});
};

We want your feedback!

Have a suggestion? Have a complaint? Confused about something in the documentation? Just want to say hi?

We want to make Pinata the best product available. That involves listening to our users and addressing their needs.

Send us an email at [email protected] and we'll see how we can help.