# API Keys
This page is where you can create, record, and delete API keys for the [Pinata API](/api-reference/introduction). Creating an API key is very simple! Just visit the page to start by click on the API Keys button in the left sidebar, then click "New Key" in the top right.
In the New Key modal you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, and be sure to give it a name to keep track of it. Once you have that filled out click "Create Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT again
Once you have created your keys you can go ahead and try testing them! You can even use them in our [API Reference section](/api-reference/endpoint/ipfs/test-authentication) :eyes: Or feel free to paste this into your terminal with your `JWT`
```bash cURL
curl --request GET \
--url https://api.pinata.cloud/data/testAuthentication \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see this!
```shell bash
{
"message": "Congratulations! You are communicating with the Pinata API!"
}
```
## Managing Keys
From the Keys Page you can see the name of a key, the public key, when it was issues, how many max uses it has, and what permissions it was given.
At any point you can delete an API key by clicking on the Revoke button
# Billing
The billing page is where you can upgrade your account, view your current usage, or make changes to your billing info.
## Usage
Scrolling down to "Usage overview," this is where you can view how much of your plan has been used in the month. Gateway Bandwidth and Requests are reset each month on your billing cycle date.
If you reach 80% percent of your usage available, then you will start to
receive emails and warnings that you are close to running out of space. If you
are on the Free plan, then your account will no longer be able to upload or
use the Dedicated Gateway once your account has gone above the limit by 25%.
## Payment Info
Clicking the 'Manage Billing' button will show you the current card in use and if it's the default. If you want to remove a card, then you will need to add a new one first and set it as default before removing the old one.
Pinata currently only accepts standard debit and credit cards
## Plan Selection
From the plan selection you can choose a plan that fits your need the most, whether that be upgrading or downgrading.
If you upgrade in the middle of a billing cycle, then you will only be charged
the prorated amount
# Limits
The Files API and IPFS API have variying limits that users should be aware of.
## API Limits
API rate limits on both the Files API and IPFS API are currently determined by plan type:
| Plan | Rate Limit |
| ------ | ----------------------- |
| Free | 60 requests per minute |
| Picnic | 250 requests per minute |
| Fiesta | 500 requests per minute |
### Exceptions
The following API calls have increased rate limits:
* Endpoints under `api.pinata.cloud/data/` have a rate limit of 30 requests per minute
* The [Pinning Services API endpoint for listing content](/api-reference/pinning-service-api) has a rate limit of 30 requests per minute
## File Restrictions
HTML and binary file uploads are currently only available on paid plans with granted access. If you are on a paid plan and wish to upload HTML please send a request through our support chat or send an email to [team@pinata.cloud](mailto:team@pinata.cloud)
## Gateway Rate Limits
At this time there are currently no rate limits for users retrieving content from a dedicated gateway.
## Upload Size Limits
There differing limits on file sizes between the Files API and IPFS API
### Files API
Files that are over **100MB** will require using [resumable uploads](/files/uploading-files#resumable-uploads) to complete. If you are using the SDK and the method `upload.file()` this will be handled automatically.
Beyond 100MB the max file size is **25GB** at this time.
### IPFS API
While the upload limit is 25GB we would recommend only uploading up to 15GB per file/folder for reliability reasons. We can try to assist uploads 15GB-25GB but we cannot guarantee success at this time.
There is no aggregate limit for uploads, but each individual upload (whether it is a file or a folder) is limited to **25 GB**.
There is also a file limit size of **10MB** for the pinJSONToIPFS API endpoint.
# Workspaces
Workspaces is only available on the [Picnic and Fiesta plans](https://pinata.cloud/pricing)
Workspaces is a feature that allows you to add multiple people to your account and collaborate in a natural way. With the Picnic plan, you'll get 3 seats to invite your teammates, and with Fiesta you'll get 5 seats, plus the ability to add more at an extra fee.
## Inviting Members
To get started, login with a paid account and click on the profile button in the top right, then select "Workspaces."
Once at the Workspaces screen, you can type in the email for the person you want to invite. They could already have a Pinata account or could be someone who hasn't signed up yet. Once they sign into their account, they will be prompted to accept the invite.
## Switching Workspaces
By default, when you login, you will be put in your account with your Workspace, and you can switch to another Workspace you are member of by clicking on the drop-down menu in the top left corner.
## Removing Members
If you ever need to remove someone from a Workspace, you can do so from the Workspaces page. Click on the three small dots next to the user's email and click "remove member." You can invite them back at any time!
# Add File To Group
put /files/groups/{id}/ids/{file_id}
# Add Swap
put /files/swap/{cid}
# Create Group
post /files/groups
# Delete File by ID
delete /files/{id}
# Delete Group
delete /files/groups/{id}
# Get File by ID
get /files/{id}
# Get Group
get /files/groups/{id}
# Create Signed URL
post /files/sign
# Get Swap History
get /files/swap/{cid}
# Add CIDs to Group
put /groups/{id}/cids
# Add Signature to CId
post /ipfs/signature/{cid}
# Add Swap
put /ipfs/swap/{cid}
# Create Custom Domain for Gateway
post /ipfs/gateways/{id}/custom_domain
# Create Gateway
post /ipfs/gateways
# Create Gateway Host Origin Restriction
post /ipfs/gateways/{id}/hosts
# Create Gateway IP Address Restriction
post /ipfs/gateways/{id}/ips
# Create Gateway Key Restriction
post /ipfs/gateways/{id}/access_tokens
# Create Group
post /groups
# Delete Gateway
delete /ipfs/gateways/{id}
# Delete Gateway Custom Domain
delete /ipfs/gateways/{id}/custom_domain/{custom_domain_id}
# Delete Group
delete /groups/{id}
# Gateway Custom Domain Details
get /ipfs/gateways/{id}/custom_domain/{custom_domain_id}
# Gateway Details
get /ipfs/gateways/{id}
# Gateway Domain Available
get /ipfs/gateways/exists/{domain}
# Get a Group
get /groups/{id}
# Get Marketplace Plugin Details
get /ipfs/plugins_marketplace/{id}
# Get Signature for a CID
get /ipfs/signature/{cid}
# Get Swap History
get /ipfs/swap/{cid}
# Install Gateway Plugin
post /ipfs/gateway_plugins/{gateway_id}
# List Files
get /data/pinList
List all the files on your Pinata account
For more detailed information on listing and querying your files on IPFS, visit the [Listing Files doc](/web3/pinning/listing-files).
# List Gateway Plugins
get /ipfs/gateway_plugins
# List Gateways
get /ipfs/gateways
# List Groups
get /groups
# List Installed Plugins for Gateway
get /ipfs/gateway_plugins/{gateway_id}
# List Marketplace Plugins
get /ipfs/plugins_marketplace
# List Pin By CID Jobs
get /pinning/pinJobs
List all currently running pinByHash jobs
# Pin By CID
post /pinning/pinByHash
Upload a file already on the IPFS network to Pinata
This endpoint allows for an additional property in the `pinataOptions` object to help our IPFS nodes find the content you would like pinned.
**`hostNodes` - multiaddresses of nodes your content is already stored on.**
You can pass in the "multiaddresses" up to five host nodes that your content already resides on.
To find the multiaddresses of your own nodes, simply run the following on your node's command line:
`ipfs id`
In the response, you'll want to focus on the "Addresses" array that's returned. Here you'll find the multiaddresses of your node. These multiaddresses are what other IPFS nodes use to connect to your node.
In the "Addresses" array, take note of the multiaddress that contains your external IP address. Not the local ipv4 "127.0.0.1" address or the local ipv6 "::1" address.
Here's an example of a full external ipv4 multiaddress (your IP address and node ID will differ):
`/ip4/123.456.78.90/tcp/4001/ipfs/QmAbCdEfGhIjKlMnOpQrStUvWxYzAbCdEfGhIjKlMnOpQr`
Once you've grabbed the full multiaddress for every node that already has your content pinned, simply add the "host\_nodes" property to your pinataOptions object like so:
```
{
hashToPin: (ExampleHash),
pinataOptions: {
hostNodes: [
/ip4/hostNode1ExternalIP/tcp/4001/ipfs/hostNode1PeerId,
/ip4/hostNode2ExternalIP/tcp/4001/ipfs/hostNode2PeerId
.
.
.
]
}
}
```
# Pin File to IPFS
post /pinning/pinFileToIPFS
Upload a file to Pinata to be pinned to IPFS
HTML uploads are currently only available on paid plans with granted access. If you are on a paid plan and wish to upload HTML please send a request through our support chat or send an email to [team@pinata.cloud](mailto:team@pinata.cloud)
### Guides
Usually you can pass a Blob directly into the request but to help guarantee success we recommend passing it into a `File` object.
```javascript Pin Blob
const JWT = 'YOUR_PINATA_JWT';
async function pinFileToIPFS() {
try {
const text = "Hello World!";
const blob = new Blob([text], { type: "text/plain" });
const file = new File([blob], "hello-world.txt")
const data = new FormData();
data.append("file", file);
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);
}
};
await pinFileToIPFS()
```
To upload a file from an external URL you can stream the contents into an `arrayBuffer`, which then gets passed into a new `Blob` that can then be uploaded to Pinata.
```javascript Upload by URL
const JWT = "YOUR_PINATA_JWT";
async function uploadByURL(url) {
try {
const urlStream = await fetch(url);
const arrayBuffer = await urlStream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const file = new File([blob], "file")
const data = new FormData();
data.append("file", file);
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);
}
}
await uploadByURL("https://pocketcast.cloud/og.png");
```
To upload a file in base64 simply turn the contents into a `buffer` that is passed into a `Blob`.
```javascript Upload base64
const JWT = "YOUR_PINATA_JWT";
async function uploadBase64(base64String) {
try {
const buffer = Buffer.from(base64String, "base64");
const blob = new Blob([buffer]);
const file = new File([blob], "file");
const data = new FormData();
data.append("file", file);
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);
}
}
await uploadBase64("SGVsbG8gZnJvbSBQaW5hdGEhIDop");
```
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.
```javascript Node.js
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()
```
```javascript React
import { useState } from "react";
function App() {
const [selectedFile, setSelectedFile]: any = useState();
const changeHandler = (event: any) => {
setSelectedFile(event.target.files);
};
const handleSubmission = async () => {
try {
const formData = new FormData();
Array.from(selectedFile).forEach((file) => {
formData.append("file", file);
});
const metadata = JSON.stringify({
name: "File name",
});
formData.append("pinataMetadata", metadata);
const options = JSON.stringify({
cidVersion: 0,
});
formData.append("pinataOptions", options);
const res = await fetch(
"https://api.pinata.cloud/pinning/pinFileToIPFS",
{
method: "POST",
headers: {
Authorization: `Bearer ${import.meta.env.VITE_PINATA_JWT}`,
},
body: formData,
}
);
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
return (
<>
>
);
}
export default App;
```
```javascript Javascript
import FormData from "form-data"
const pinDirectoryToIPFS = async () => {
try {
const folder = "json";
const json1 = { hello: "world" };
const json2 = { hello: "world2" };
const blob1 = new Blob([JSON.stringify(json1, null, 2)], {
type: "application/json",
});
const blob2 = new Blob([JSON.stringify(json2, null, 2)], {
type: "application/json",
});
const files = [
new File([blob1], "hello.json", { type: "application/json" }),
new File([blob2], "hello2.json", { type: "application/json" }),
];
const data = new FormData();
Array.from(files).forEach((file) => {
// If you are not using `fs` you might need to specify the folder path along with the filename
data.append("file", file, `${folder}/${file.name}`);
});
const pinataMetadata = JSON.stringify({
name: `${folder}`,
});
data.append("pinataMetadata", pinataMetadata);
const res = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
method: "POST",
headers: {
Authorization: `Bearer ${PINATA_JWT}`,
},
body: data,
});
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
pinDirectoryToIPFS();
```
# Pin JSON
post /pinning/pinJSONToIPFS
Uploads a JSON object to Pinata and pins it to IPFS
Pinata makes it easy to upload JSON objects using the `pinJSONToIPFS` endpoint.
```javascript Pin JSON
const JWT = 'YOUR_PINATA_JWT';
async function pinJSONToIPFS() {
try {
const data = JSON.stringify({
pinataContent: {
name: "Pinnie",
description: "A really sweet NFT of Pinnie the Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
external_url: "https://pinata.cloud"
},
pinataMetadata: {
name: "metadata.json"
}
})
const res = await fetch("https://api.pinata.cloud/pinning/pinJSONToIPFS", {
method: "POST",
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${JWT}`,
},
body: data,
});
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
await pinJSONToIPFS()
```
# Remove CIDs from a Group
delete /groups/{id}/cids
# Remove Signature from CID
delete /ipfs/signature/{cid}
# Remove Swap
delete /ipfs/swap/{cid}
# Revoke Gateway Key
delete /ipfs/gateways/{id}/access_tokens/{access_token_id}
# Revoke Host Origin Restriction
delete /ipfs/gateways/{id}/hosts/{host_id}
# Revoke IP Address Restricton
delete /ipfs/gateways/{id}/ips/{ip_id}
# Test Authentication
get /data/testAuthentication
Test your API keys and your ability to connect to the Pinata API
# Time Interval Gateway Analytics
get /ipfs/gateway_analytics_time_series
# Top Gateway Analytics
get /ipfs/gateway_analytics_top
# Uninstall Gateway Plugin
delete /ipfs/gateway_plugins/{gateway_id}/plugin/{plugin_id}
# Delete File (Unpin)
delete /pinning/unpin/{CID}
Delete a file by CID
If you find yourself in a place where you need to unpin a lot of files or perhaps all your files, you can use a script like this to create an array of CIDs and unpin them one by one. The example below uses the [`pinList`](/api-reference/endpoint/ipfs/list-files) queries to target all pinned files and return 1000 for each request. This could easily be done with a different query to target different files, please check out the [listing files](/web3/pinning/listing-files) doc for more info.
```javascript Unpin All Files
const PINATA_JWT = "YOUR_JWT_HERE";
const PIN_QUERY = `https://api.pinata.cloud/data/pinList?status=pinned&pageLimit=1000&includeCount=false`;
const fetch = require("node-fetch");
const wait = (milliseconds) => {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
};
const fetchPins = async () => {
try {
console.log("Fetching pins...");
let pinHashes = [];
let pageOffset = 0;
let hasMore = true;
while (hasMore === true) {
try {
const response = await fetch(`${PIN_QUERY}&pageOffset=${pageOffset}`, {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${PINATA_JWT}`,
},
});
const responseData = await response.json();
const rows = responseData.rows;
if (rows.length === 0) {
hasMore = false;
}
const itemsReturned = rows.length;
pinHashes.push(...rows.map((row) => row.ipfs_pin_hash));
pageOffset += itemsReturned;
await wait(300);
} catch (error) {
console.log(error);
break;
}
}
console.log("Total pins fetched: ", pinHashes.length);
return pinHashes;
} catch (error) {
console.log(error);
}
};
const deletePins = async () => {
const pinHashes = await fetchPins();
const totalPins = pinHashes.length;
let deletedPins = 0;
try {
for (const hash of pinHashes) {
try {
const response = await fetch(
`https://api.pinata.cloud/pinning/unpin/${hash}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${PINATA_JWT}`,
},
}
);
await wait(300);
deletedPins++;
process.stdout.write(`Deleted ${deletedPins} of ${totalPins} pins\r`);
} catch (error) {
console.log(error);
}
}
console.log("Pins deleted");
} catch (error) {
console.log(error);
}
};
deletePins();
```
# Update File Metadata
put /pinning/hashMetadata
# Update Group
put /groups/{id}
# List Files
get /files
# List Groups
get /files/groups
# Remove File From Group
delete /files/groups/{id}/ids/{file_id}
# Remove Swap
delete /files/swap/{cid}
# Update File
put /files/{id}
# Update Group
put /files/groups/{id}
# null
post /files
# Create API Key
post /pinata/keys
# List API Keys
get /pinata/keys
# Revoke API Key
put /pinata/keys/{key}
# Introduction
Getting started with the Pinata API
Welcome to the Pinata API Reference! Before you can do anything in the API, you'll need some API Keys.
## API Keys
Visit the [Pinata API Keys](https://app.pinata.cloud/developers/api-keys) page to generate new keys.
In the 'New Key' modal, you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, so be sure to give it a name to keep track of it. Once you have that filled out, click "Generate API Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT
again
Once you have created your keys you can go ahead and try testing them! Try to paste this into your terminal with your `JWT`
```bash
curl --request GET \
--url https://api.pinata.cloud/data/testAuthentication \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see this!
```shell bash
{
"message": "Congratulations! You are communicating with the Pinata API!"
}
```
# Pinning Service API
Not to be confused with the IPFS API, the Pinning Service API allows you to create a link between your local IPFS node and Pinata
## IPFS Pinning Services API Spec Compatibility
The [IPFS Pinning Services API Spec](https://ipfs.github.io/pinning-services-api-spec/) is a standardized specification for developers building on top of IPFS that allows an application to integrate a pinning service without needing to learn that pinning service's unique API.
### Endpoints
Pinata users looking to utilize the IPFS Pinning Services API can do so from our dedicated API endpoint:
`https://api.pinata.cloud/psa`
For an up-to-date list of available endpoints, as well as current documentation, please visit the [Official Endpoint Documentation](https://ipfs.github.io/pinning-services-api-spec/#tag/pins).
### Authentication
To authenticate with Pinata through the Pinning Services API spec, you'll first need to have an `accessToken`. You can create this API token on the [Pinata Keys Page](https://pinata.cloud/keys).
When you create a new API key, you'll need to take note of the JWT ([JSON Web Token](https://jwt.io/)) token that's shown to you immediately after creation. This JWT is specific to the API key and shares the same permissions.
If you ever revoke the API key for this JWT, this JWT will no longer be valid for authenticating to the Pinning Services API.
**Configuring Pinata in the IPFS Desktop App**
If you are running the IPFS desktop app, you can configure your pinning service of choice within the user interface. To do so, open the app, go to your Preferences, then click Add Service:
Select Pinata as the pinning service, and then configure with your secret access token (JWT):
From there you can upload files and have the option to pin locally as well as remotely to Pinata!
### Configuring Pinata in the IPFS CLI
You can also pin to Pinata directly from the IPFS CLI using the `ipfs` command.
To add Pinata credentials, use the following command (where YOUR\_JWT is the JWT token described in the “Authentication” section above):
```bash
ipfs pin remote service add pinata https://api.pinata.cloud/psa YOUR_JWT
```
To pin a CID to Pinata under a human-readable name:
```bash
ipfs pin remote add --service=pinata --name=war-and-peace.txt bafybeib32tuqzs2wrc52rdt56cz73sqe3qu2deqdudssspnu4gbezmhig4
```
To list successful pins:
```bash
ipfs pin remote ls --service=pinata
```
To list pending pins:
```bash
ipfs pin remote ls --service=pinata --status=queued,pinning,failed
```
For more commands and general help:
```bash
ipfs pin remote --help
```
# Deleting Files
Deleting files from Pinata is simple and easy!
## Deleting Programatically
The SDK has a very simple [delete](/sdk/files/delete) method that will allow you to delete an array of files by `id`. Alternatively you can delete a single file with the [API](/api-reference/introduction).
```typescript SDK
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const unpin = await pinata.files.delete([
"3c52f1b8-11b1-40d9-849d-5f05a4bbd76d",
"b72886db-9dd4-434c-a1b2-f9d36781ecee"
])
```
```typescript API {6,9,11}
const JWT = "YOUR_PINATA_JWT";
async function delete() {
try {
const fileId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const request = await fetch(
`https://api.pinata.cloud/v3/files/${fileId}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Deleting All Files
If you find yourself in a position where you need to delete most or all of your files you can use the [Auto Paginate](/sdk/files/list#auto-paginate) feature on the SDK to fetch all the IDs of your files and delete them in a few lines of code!
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "dweb.mypinata.cloud",
});
async function main() {
try {
let files = [];
for await (const item of pinata.files.list()) {
files.push(item.id);
}
const res = await pinata.files.delete(files);
} catch (error) {
console.log(error);
}
}
main();
```
## Deleting by Web App
If you are trying to delete files you can do so by clicking on the "more" button and selecting "Delete"
Additionally, with our Bulk File Actions tool, you can select and manage multiple files at once - up to 100!
# File Groups
Groups allow you to organize your Pinata content through the Pinata App or through the Files API, giving you a clearer picture of what your files are being used for.
## Public vs Private Groups
Groups help determine if a file uploaded through the Files API is private or publicly accessible. All file uploads are private by default, unless a file is:
* Uploaded to a public group
* Added to a public group
* Is part of a group that is updated to public
At this point the file can be accessed publicly without a signed URL. For more info read the docs on [retrieving files](/gateways/retrieving-files).
Groups can either be public upon creation or updated after the fact, as laid out below.
## Files SDK and API
With the [Files SDK](/sdk/groups), you can create groups, add files to groups, list details about a group, and more! You can also mange groups using the [Files API](/api-reference/endpoint/create-group).
### Create a Group
To create a group you can use the [create](/sdk/groups/create) method and passing in the `name` you want to give a group.
```typescript SDK
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.create({
name: "My New Group",
isPublic: true
});
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const payload = JSON.stringify({
name: "My New Group",
isPublic: true
})
const request = await fetch("https://api.pinata.cloud/v3/files/groups", {
method: "POST",
headers: {
"Content-Type": "application/json"
Authorization: `Bearer ${JWT}`,
},
body: payload,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This will return the Group info
```typescript SDK
{
id: "01919976-955f-7d06-bd59-72e80743fb95",
name: "Test Private Group",
is_public: true,
created_at: "2024-08-28T14:49:31.246596Z"
}
```
```json API
{
"data": {
"id": "01919976-955f-7d06-bd59-72e80743fb95",
"name": "Test Private Group",
"public": false,
"created_at": "2024-08-28T14:49:31.246596Z"
}
}
```
### Add or Remove Files from a Group
There are two ways you can add files to a group. The first is to add the file to a group on [upload](/sdk/upload/file).
```typescript SDK {3}
const upload = await pinata.upload
.file(file)
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
```typescript API {11}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
formData.append("group", "b07da1ff-efa4-49af-bdea-9d95d8881103")
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
Another option is to add files after the fact using the [addFiles](/sdk/groups/add-files) method.
```typescript SDK
const upload = await pinata.groups.addFiles({
groupId: "b07da1ff-efa4-49af-bdea-9d95d8881103",
files: [
"0ed5738f-07e7-4587-81fb-f04f8be15d77",
"a277dc29-2ca3-4dfb-aeb9-3f2b23e956f7"
]
})
```
```typescript API {6,8,11}
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const groupId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const fileId = "521f23f3-2749-4611-b757-3155b40ff570"
const request = await fetch(
`https://api.pinata.cloud/v3/files/groups/${groupId}/ids/${fileId}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
Removing files can be done the exact same way with the [removeFiles](/sdk/groups/remove-files) method.
```typescript SDK
const upload = await pinata.groups.removeFiles({
groupId: "b07da1ff-efa4-49af-bdea-9d95d8881103",
files: [
"0ed5738f-07e7-4587-81fb-f04f8be15d77",
"a277dc29-2ca3-4dfb-aeb9-3f2b23e956f7"
]
})
```
```typescript API {6,8,11}
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const groupId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const fileId = "521f23f3-2749-4611-b757-3155b40ff570"
const request = await fetch(
`https://api.pinata.cloud/v3/files/groups/${groupId}/ids/${fileId}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Get a Group
To fetch details of an already existing group you can use the [get](/sdk/groups/get) and pass in the `groupId`.
```typescript SDK
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
```typescript API {6,8,11}
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const groupId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const request = await fetch(
`https://api.pinata.cloud/v3/files/groups/${groupId}`,
{
method: "GET",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This will return the same group info received upon creation.
```typescript SDK
{
id: "0191997b-ca28-79e8-9dbc-a8044ad3e547",
name: "My New Group 5",
is_public: false,
created_at: "2024-08-28T14:55:12.448504Z",
}
```
```json APi
{
"data": {
"id": "0191997b-ca28-79e8-9dbc-a8044ad3e547",
"name": "My New Group 5",
"is_public": false,
"created_at": "2024-08-28T14:55:12.448504Z"
}
}
```
### List All Groups
If you want to get all Groups or filter through them, you can use the [list](/sdk/groups/list) method.
```typescript SDK
const groups = await pinata.groups.list()
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const url = "https://api.pinata.cloud/v3/files/groups"
const request = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
Results can be filtered with the following queries.
#### name
* Type: `boolean`
Filters groups based on the group name
```typescript SDK
const groups = await pinata.groups
.list()
.name("SDK")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files/groups?name=SDK"
```
#### isPublic
* Type: `boolean`
Filters groups based on whether they are public or not
```typescript SDK
const groups = await pinata.groups
.list()
.isPublic(true)
```
```typescript API
const url = "https://api.pinata.cloud/v3/files/groups?isPublic=true"
```
#### limit
* Type: `number`
Limits the number of results
```typescript SDK
const groups = await pinata.groups
.list()
.limit(10)
```
```typescript API
const url = "https://api.pinata.cloud/v3/files/groups?limit=10"
```
This will return an array of Groups and their respective info:
```typescript SDK
{
groups: [
{
id: "0191997b-ca28-79e8-9dbc-a8044ad3e547",
name: "My New Group 5",
is_public: false,
created_at: "2024-08-28T14:55:12.448504Z",
}
],
next_page_token: "MDE5MWIzNGMtMWNmNy03MzExLThmMjYtZmZlZDMzYTVlY"
}
```
```json API
{
"groups": [
{
"id": "0191997b-ca28-79e8-9dbc-a8044ad3e547",
"name": "My New Group 5",
"is_public": false,
"created_at": "2024-08-28T14:55:12.448504Z"
}
],
"next_page_token": "MDE5MWIzNGMtMWNmNy03MzExLThmMjYtZmZlZDMzYTVlY"
}
```
### Updating a Group
You can update the name of a group or it's public status using the [update](/sdk/groups/update) method and passing in the `groupId, a new `name`you want to use, or setting the`isPublic\` boolean.
```typescript SDK
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
name: "My New Group 2",
isPublic: true
});
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const groupId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const payload = JSON.stringify({
name: "My New Group 2",
is_public: true
})
const request = await fetch(
`https://api.pinata.cloud/v3/files/groups/${groupId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json"
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This will return the updated Group info.
```typescript SDK
{
id: "3778c10d-452e-4def-8299-ee6bc548bdb0",
name: "My New Group 2",
is_public: false,
created_at: "2024-08-28T20:58:46.96779Z"
}
```
```json API
{
"data": {
"id": "01919ac8-a6f5-7e8e-a8a2-6cfe00122b90",
"name": "Updated Name",
"is_public": true,
"created_at": "2024-08-28T20:58:46.96779Z"
}
}
```
### Delete a Group
Deleting a Group that has CIDs inside of it will not unpin/delete the files. Please use the [delete](/sdk/files/delete) method to actually delete a file from your account
To delete a Group you can use the [delete](/sdk/groups/delete) method and pass in the `groupId`.
```typescript SDK
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function group() {
try {
const groupId = "e0b102e9-d481-4192-ab44-b8f7ff010e9a"
const request = await fetch(
`https://api.pinata.cloud/v3/files/groups/${groupId}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
If successful the endpoint will return an `OK` response.
# Hot Swaps
Use the Hot Swaps plugin to make one CID map to another
The Hot Swaps plugin can be used to "redirect" a CID to another CID. This can be useful when you need to replace content without updaing the CID hash. What makes this unique from just replacing content is that every swap is recorded and is available as a history. Combine that with the content addressable nature of CIDs and you get a version history of content.
To demonstrate how this plugin works, consider the following example:
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example.mypinata.cloud", // Gateway has Hot Swaps installed
});
async function main() {
try {
// Upload the first file
const file = new File(["The original CID"], "cid.txt", {
type: "text/plain",
});
const { cid: CID1 } = await pinata.upload.file(file);
console.log("This is the original CID hash: ", CID1);
// Upload a second file
const file2 = new File(["The new CID"], "cid.txt", { type: "text/plain" });
const { cid: CID2 } = await pinata.upload.file(file2);
console.log("This is the new CID hash: ", CID2);
// Create the swap, so when we visit CID1 we will get the content of CID2
const swap = await pinata.files.addSwap({
cid: CID1,
swapCid: CID2,
});
console.log("Swap created: ", swap);
// Fetch CID1 through our gateway that has Hot Swaps installed, get the content of CID2
const data = await pinata.gateways.get(CID1);
console.log("Result of requestingt CID1 through the gateway: ", data);
} catch (error) {
console.log(error);
}
}
main();
```
## Installation
To install a plugin navigate to the Plugins Marketplace tab on the right side.
Once there you can find the plugin you want to install and click "Install." This will bring up a drop down of your Gateways to choose which the plugin is installed to.
Once installed you can confirm its there by going to the "My Plugins" tab.
## Usage
After installing the plugin you can then make CID swaps and have them reflect when making Gateway requests. The first parameter `cid` will be the original CID, and `swapCid` will be the content you want it to point to instead.
```typescript SDK {8-11}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const swap = await pinata.files.addSwap({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
```typescript API {6,8,10-12}
const JWT = "YOUR_PINATA_JWT";
async function swap() {
try {
const cid = "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
const url = `https://api.pinata.cloud/v3/files/swap/${cid}`
const payload = JSON.stringify({
swap_cid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
const request = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
You can fetch the history of CID swaps using the `getSwapHistory` method, passing in the `cid` of the original CID and the `domain` of the gateway that has the Hot Swaps plugin installed.
```typescript SDK
const history = await pinata.files.getSwapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
```
```typescript API {6,8,10}
const JWT = "YOUR_PINATA_JWT";
async function swap() {
try {
const cid = "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
const query = "?domain=discordpinnie.mypinata.cloud"
const url = `https://api.pinata.cloud/v3/files/swap/${cid}` + query
const request = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
The response will show a history of the CID and domain in question
```typescript SDK
{
data: [
{
mapped_cid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
created_at: "2024-08-19T14:34:46.492432Z"
},
{ mapped_cid: null, created_at: "2024-08-19T14:25:10.208726Z" },
{
mapped_cid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
created_at: "2024-08-19T00:23:41.755206Z"
}
]
}
```
```json API
{
"data": [
{
"mapped_cid": "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
"created_at": "2024-08-19T14:34:46.492432Z"
},
{ "mapped_cid": null, "created_at": "2024-08-19T14:25:10.208726Z" },
{
"mapped_cid": "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
"created_at": "2024-08-19T00:23:41.755206Z"
}
]
}
```
To delete a CID swap you can simply use the `deleteSwap` method and pass in the CID.
```typescript
const deleteSwap = await pinata.gateways.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
```
```typescript API {6,8}
const JWT = "YOUR_PINATA_JWT";
async function swap() {
try {
const cid = "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
const url = `https://api.pinata.cloud/v3/files/swap/${cid}`
const request = await fetch(url, {
method: "DELETE",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
# Key-Values
A unique and powerful feature included with the Files API and IPFS API is the key-value store. Anytime you upload or update a file you can store up to 10 key-value pairs.
```typescript SDK {3-8}
const upload = await pinata.upload
.file(file)
.addMetadata({
keyvalues: {
env: "prod",
userId: "abc123"
}
})
```
```typescript API {11-15,17}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const keyvalues = JSON.stringify({
keyvalues: {
env: "prod",
userId: "abc123"
}
})
formData.append("keyvalues", keyvalues)
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This small yet powerful feature allows you to remove the need for an external database in most cases. We like to call this paradigm **[File-Centric Architecture](https://pinata.cloud/blog/using-file-centric-architecture-to-build-simple-and-capable-apps/)**, where apps and their structure revolves around the files themselves. This creates a molecule like structure and keeps the data related to the file close by.
![cover](https://dweb.mypinata.cloud/files/bafybeieo4ww5lykpsegfgmse5o2d5s5onfvlkglzqmvbeldb2dkx26z2ve)
## Creating
Creating a new key-value for a file can be done in two ways:
### Uploading a File
By including the key-values as part of the upload [method](/sdk/upload/file) or [endpoint](/api-reference/endpoint/upload-a-file) and the file and the key-values will be created at the same time.
```typescript SDK {3-8}
const upload = await pinata.upload
.file(file)
.addMetadata({
keyvalues: {
env: "prod",
userId: "abc123"
}
})
```
```typescript API {11-15,17}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const keyvalues = JSON.stringify({
keyvalues: {
env: "prod",
userId: "abc123"
}
})
formData.append("keyvalues", keyvalues)
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Updating an Existing File
If you've already uploaded a file and want to add a key-value you can do so with the update [method](/sdk/files/update) or [endpoint](/api-reference/endpoint/update-file).
```typescript SDK {3-7}
const update = await pinata.files.update({
id: "2b4ee88d-1032-4e4e-a373-97d1ab127f16", // Target File ID
keyvalues: {
env: "prod",
userId: "abc123"
}
})
```
```typescript API {6,8-13,15-21}
const JWT = "YOUR_PINATA_JWT";
async function update() {
try {
const fileId = "2b4ee88d-1032-4e4e-a373-97d1ab127f16"
const data = JSON.stringify({
keyvalues: {
env: "prod",
userId: "abc123"
}
})
const request = await fetch(`https://api.pinata.cloud/v3/files/${fileId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: data,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Retrieving
Since key-values exist with files, you can retrieve them by listing files either through the SDK [method](/sdk/files/list) or API [endpoint](/api-reference/endpoint/list-files), and filtering results by key-value. The operator will always be `===`.
You can chain multiple key-value queries together and it will only return files that meet both values.
```typescript SDK {3-5}
const files = await pinata.files
.list()
.metadata({
user: "abc123"
})
```
```typescript API {5}
const JWT = "YOUR_PINATA_JWT";
async function list() {
try {
const request = await fetch(`https://api.pinata.cloud/v3/files?metadata[user]=123`, {
method: "GET",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Updating
The Files API key-value system will automatically detect if you are replacing an existing value for a given key. For example, if you have a key of `env` with a value of `prod`, if you make an update of `env: "dev"` it will replace the old value. If the key does not exist then it will make a new key-value entry.
```typescript SDK {4}
const update = await pinata.files.update({
id: "2b4ee88d-1032-4e4e-a373-97d1ab127f16", // Target File ID
keyvalues: {
env: "dev", // Previously `prod`
}
})
```
```typescript API {10}
const JWT = "YOUR_PINATA_JWT";
async function update() {
try {
const fileId = "2b4ee88d-1032-4e4e-a373-97d1ab127f16"
const data = JSON.stringify({
keyvalues: {
env: "dev", // Previously `prod`
}
})
const request = await fetch(`https://api.pinata.cloud/v3/files/${fileId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: data,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Deleting
You can remove a key-value entry by making the value `null`.
```typescript SDK {4}
const update = await pinata.files.update({
id: "2b4ee88d-1032-4e4e-a373-97d1ab127f16", // Target File ID
keyvalues: {
env: null, // Deletes the `env` key-value entry
}
})
```
```typescript API {6,8-13,15-21}
const JWT = "YOUR_PINATA_JWT";
async function update() {
try {
const fileId = "2b4ee88d-1032-4e4e-a373-97d1ab127f16"
const data = JSON.stringify({
keyvalues: {
env: null, // Deletes the `env` key-value entry
}
})
const request = await fetch(`https://api.pinata.cloud/v3/files/${fileId}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: data,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Further Reading
Check out some of our reading material on some of the possibilities of key-values and file-centric architecture!
# Listing Files
Learn how to list files inside your Pinata account
To list the files on your account you can either use the [Files SDK](/sdk/data/list) or the [Files API](/api-reference/endpoint/list-files) to fetch file data programatically.
```typescript SDK
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const files = await pinata.files.list()
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function files() {
try {
const url = "https://api.pinata.cloud/v3/files",
const request = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${JWT}`,
}
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This will return an array of file objects
```typescript SDK
{
files: [
{
id: "dd5f8888-bf15-4559-b8a2-6c626869507f",
name: "Hello Files API",
cid: "bafybeifq444z4b7yqzcyz4a5gspb2rpyfcdxp3mrfpigmllh52ld5tyzwm",
size: 4861678,
number_of_files: 1,
mime_type: "TODO",
group_id: null,
created_at: "2024-08-27T14:57:51.485934Z",
},
{
id: "e2057aa3-7b6c-4a45-b785-12ba297bcbd0",
name: "Quickstart.png",
cid: "bafkreiebavn2jzkqh3ehy4pkqkdi2otnho6gbcffkeqnunk2lw5nmnwaea",
size: 223548,
number_of_files: 1,
mime_type: "TODO",
group_id: "5f8adce6-7312-46e0-90f7-13896bed297d",
created_at: "2024-08-28T23:46:07.823118Z",
},
{
id: "ac5308a1-de49-40a3-9f5c-d20f1bb6206d",
name: "hello.txt",
cid: "bafkreiffsgtnic7uebaeuaixgph3pmmq2ywglpylzwrswv5so7m23hyuny",
size: 11,
number_of_files: 1,
mime_type: "TODO",
group_id: null,
created_at: "2024-08-29T02:23:02.735018Z",
}
],
next_page_token: "MDE5MWIzNGMtMWNmNy03MzExLThmMjYtZmZlZDMzYTVlY"
}
```
```json API
{
"files": [
{
"id": "dd5f8888-bf15-4559-b8a2-6c626869507f",
"name": "Hello Files API",
"cid": "bafybeifq444z4b7yqzcyz4a5gspb2rpyfcdxp3mrfpigmllh52ld5tyzwm",
"size": 4861678,
"number_of_files": 1,
"mime_type": "TODO",
"group_id": null,
"created_at": "2024-08-27T14:57:51.485934Z"
},
{
"id": "e2057aa3-7b6c-4a45-b785-12ba297bcbd0",
"name": "Quickstart.png",
"cid": "bafkreiebavn2jzkqh3ehy4pkqkdi2otnho6gbcffkeqnunk2lw5nmnwaea",
"size": 223548,
"number_of_files": 1,
"mime_type": "TODO",
"group_id": "5f8adce6-7312-46e0-90f7-13896bed297d",
"created_at": "2024-08-28T23:46:07.823118Z"
},
{
"id": "ac5308a1-de49-40a3-9f5c-d20f1bb6206d",
"name": "hello.txt",
"cid": "bafkreiffsgtnic7uebaeuaixgph3pmmq2ywglpylzwrswv5so7m23hyuny",
"size": 11,
"number_of_files": 1,
"mime_type": "TODO",
"group_id": null,
"created_at": "2024-08-29T02:23:02.735018Z"
}
],
"next_page_token": "MDE5MWIzNGMtMWNmNy03MzExLThmMjYtZmZlZDMzYTVlY"
}
```
## Filters
When listing files there a few ways you can filter the results
### name
* Type: `string`
Filter results based on name
```typescript SDK {3}
const files = await pinata.files
.list()
.name("pinnie")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?name=pinnie"
```
### group
* Type: `string`
Filter results based on group ID
```typescript SDK {3}
const files = await pinata.files
.list()
.group("5b56981c-7e5b-4dff-aeca-de784728dddb")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?group=5b56981c-7e5b-4dff-aeca-de784728dddb"
```
### cid
* Type: `string`
Filter results based on CID
```typescript SDK{3}
const files = await pinata.files
.list()
.cid("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?cid=bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
```
### mimeType
* Type: `string`
Filter results based on mime type
```typescript SDK {3}
const files = await pinata.files
.list()
.mimeType("image/png")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?mimeType=image/png"
```
### order
* Type: `"ASC" | "DESC"`
Order results either ascending or descending by created date
```typescript SDK {3}
const files = await pinata.files
.list()
.order("ASC")
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?order=ASC"
```
### limit
* Type: `number`
Limit the number of results
```typescript SDK {3}
const files = await pinata.files
.list()
.limit(10)
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?limit=10"
```
### cidPending
* Type: `boolean`
Filters results and only returns files where `cid` is still `pending`
```typescript SDK {3}
const files = await pinata.files
.list()
.cidPending(true)
```
```typescript API
const url = "https://api.pinata.cloud/v3/files?cidPending=true"
```
## Auto Paginate (SDK)
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.files.list() {
console.log(item.id);
}
```
Works like magic ✨
# Uploading Files
At the core of Pinata is file uploads through our File API. It's fast and easy to use, whether you're using the [SDK](/sdk) or the [API](/api-reference). Something unique about the Files API is that all files are given a unique Content Identifier or `CID`. These are not like normal IDs in that they are content addressable hashes based on the contents of the file. You can upload the same file over and over and it will always return the same CID. This has multiple benefits including:
* Deduplication: Since a duplicate file would have the same CID, it is not included upon upload and reduces the amount of files in your account. This keeps things organized and reduces your total costs.
* Versioning: Tracking the history of a file requires proprietary solutions built on top of the data storage provider and does not expose that history and provenance in a meaningful way to everyone who needs to access it.
Let's look at the multiple ways you can upload files!
## How to Upload Files
Uploading files with Pinata is simple, whether you want to use the SDK or the API. Key things to know:
* Uploads are done through `multipart/form-data` requests
* The SDK and API accept File objects per the [Web API Standard for Files]()
* You can add additional info to your upload such as a custom name for the file, keyvalue metadata, and a target group destination for organization
Here is a simple example of how you might upload a file in Typescript
```typescript SDK
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
```
```typescript API
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
This will return the following response
```typescript SDK
{
id: "349f1bb2-5d59-4cab-9966-e94c028a05b7",
name: "file.txt",
cid: "bafybeihgxdzljxb26q6nf3r3eifqeedsvt2eubqtskghpme66cgjyw4fra",
size: 4682779,
number_of_files: 1,
mime_type: "text/plain",
user_id: "7a484d2c-4219-4f80-9d9d-86b42461e71a",
group_id: null
}
```
```JSON API
{
"data": {
"id": "349f1bb2-5d59-4cab-9966-e94c028a05b7",
"name": "file.txt",
"cid": "bafybeihgxdzljxb26q6nf3r3eifqeedsvt2eubqtskghpme66cgjyw4fra",
"size": 4682779,
"number_of_files": 1,
"mime_type": "text/plain",
"user_id": "7a484d2c-4219-4f80-9d9d-86b42461e71a",
"group_id": null
}
}
```
* `id`: The ID of the file used for getting info, updating, or deleting
* `name`: The name of the file or the provided name in the `addMetadata` method
* `cid`: A cryptographic hash based on the contents of the file
* `size`: The size of the file in bytes
* `number_of_files`: The number of files in a reference
* `mime_type`: The mime type of the uploaded file
* `user_id`: User ID of the API key that upladed the file
* `group_id`: The group the file was uploaded to if applicable
### Metadata
When uploading a file you can add additional metadata using the `addMetadata` method after the selected upload method. This can include an optional `name` override or `keyvalue` pairs that can be used to searching the file later on
[Check out the Key-Values doc for more info](/files/key-values)
```typescript SDK {3-8}
const upload = await pinata.upload
.file(file)
.addMetadata({
name: "hello.txt",
keyvalues: {
env: "prod"
}
})
```
```typescript API {11,13-17,19}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
formData.append("name", "hello.txt")
const keyvalues = JSON.stringify({
keyvalues: {
env: "prod"
}
})
formData.append("keyvalues", keyvalues)
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Groups
Pinata offers File Groups to organize your content, as well as a method to set files as public or private. You can upload a file to a group by using the `group` method.
[Check out the Groups doc for more info](/files/file-groups)
```typescript SDK {3}
const upload = await pinata.upload
.file(file)
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
```typescript API {11}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
formData.append("group", "b07da1ff-efa4-49af-bdea-9d95d8881103")
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### User Generated Keys
There are situations where you may need to upload a file client side instead of server side. A great example is in Next.js where there is a 4MB file size restriction for files passed through Next's API routes. To solve this you can use the [keys](/sdk/keys/create) method in the SDK or the [API endpoint](/api-reference/endpoint/v3-create-api-key) to create a Pinata API key with limited permissions that expire after the upload is complete. This way your admin API key stays safe behind a server.
Setting up a server side API endpoint might look something like this:
```typescript SDK
import { type NextRequest, NextResponse } from "next/server";
import { pinata } from "@/utils/config"; // Import the Files SDK instance
export const dynamic = "force-dynamic";
export async function GET() {
// Handle your auth here to protect the endpoint
try {
const uuid = crypto.randomUUID();
const keyData = await pinata.keys.create({
keyName: uuid.toString(),
permissions: {
endpoints: {
pinning: {
pinFileToIPFS: true, // Creates a key that can only upload a file
},
},
},
maxUses: 1,
})
return NextResponse.json(keyData, { status: 200 }); // Returns the key data
} catch (error) {
console.log(error);
return NextResponse.json({ text: "Error creating API Key:" }, { status: 500 });
}
}
```
```typescript API
import { type NextRequest, NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET() {
// Handle your auth here to protect the endpoint
try {
const uuid = crypto.randomUUID();
// Prepare key data for request
const keyData = await JSON.stringify({
keyName: uuid.toString(),
permissions: {
endpoints: {
pinning: {
pinFileToIPFS: true, // Creates a key that can only upload a file
},
},
},
maxUses: 1,
})
// send request and parse response
const keyRequest = await fetch("https://api.pinata.cloud/v3/pinata/keys", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.PINATA_JWT}`
},
body: keyData
})
const keyData = await keyRequest.json()
return NextResponse.json(keyData, { status: 200 }); // Returns the key data
} catch (error) {
console.log(error);
return NextResponse.json({ text: "Error creating API Key:" }, { status: 500 });
}
}
```
Then back on the client side code, you can upload and override the default API key set for the SDK using the `key` method on the `upload` class.
If using the SDK be sure to pass in the Pinata JWT key into the `.key()` method. With the API, pass in the temporary key into the headers of the upload request
```typescript SDK {3}
const keyRequest = await fetch("/api/key"); // Fetches the temporary API key
const keyData = await keyRequest.json(); // Parse response
const upload = await pinata.upload.file(file).key(keyData.JWT); // Upload the file with temp key
```
```typescript API {16}
const keyRequest = await fetch("/api/key"); // Fetches the temporary API key
const keyData = await keyRequest.json(); // Parse response
async function upload() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${keyData.JWT}`, // Uses the temporary key here
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Upload Progress
If you happen to use the API as well as local files you can also track the progress of the upload using a library like `got`. Better support for upload progress will come in later versions of the SDK!
```typescript
import fs from "fs";
import FormData from "form-data";
import got from "got";
async function upload() {
const url = `https://uploads.pinata.cloud/v3/files`;
try {
let data = new FormData();
data.append(`file`, fs.createReadStream("path/to/file"));
const response = await got(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PINATA_JWT}`,
},
body: data,
}).on("uploadProgress", (progress) => {
console.log(progress);
});
console.log(JSON.parse(response.body));
} catch (error) {
console.log(error);
}
}
```
If your file is larger than 100MB then a better approach is to follow the [Resumable Upload Guide](#resumable-uploads)
## Common File Recipes
Below are some common recipes for uploading a file.
### Blob
Usually you can pass a Blob directly into the request but to help guarantee success we recommend passing it into a `File` object.
```typescript SDK {8-10}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const text = "Hello World!";
const blob = new Blob([text]);
const file = new File([blob], "hello.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
```
```typescript API {7-9}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const text = "Hello World!";
const blob = new Blob([text]);
const file = new File([blob], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### JSON
Pinata makes it easy to upload JSON objects using the [json](/ipfs-sdk/upload/json) method.
```typescript SDK {8-15}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.json({
id: 2,
name: "Bob Smith",
email: "bob.smith@example.com",
age: 34,
isActive: false,
roles: ["user"],
});
```
```typescript API {7-16}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const json = JSON.stringify({
id: 2,
name: "Bob Smith",
email: "bob.smith@example.com",
age: 34,
isActive: false,
roles: ["user"],
})
const blob = new Blob([json]);
const file = new File([blob], "bob.json", { type: "application/json" });
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Local Files
If you need to upload files from a local file source you can use `fs` to feed a file into a `blob`, then turn that `blob` into a `File`.
```typescript SDK {10-11}
const { PinataSDK } = require("pinata");
const fs = require("fs");
const { Blob } = require("buffer");
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const blob = new Blob([fs.readFileSync("./hello-world.txt")]);
const file = new File([blob], "hello-world.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
```
```typescript API {9-10}
const JWT = "YOUR_PINATA_JWT";
const fs = require("fs");
const { Blob } = require("buffer");
async function upload() {
try {
const formData = new FormData();
const blob = new Blob([fs.readFileSync("./hello-world.txt")]);
const file = new File([blob], "hello-world.txt", { type: "text/plain" });
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### URL
To upload a file from an external URL you can stream the contents into an `arrayBuffer`, which then gets passed into a new `Blob` that can then be uploaded to Pinata. This has been abstracted in the SDK using the [url](/ipfs-sdk/upload/url) method.
```typescript SDK {8}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif");
```
```typescript API {7-10}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const stream = await fetch("https://i.imgur.com/u4mGk5b.gif")
const arrayBuffer = await stream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const file = new File([blob], "name.gif");
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### base64
To upload a file in base64 simply turn the contents into a `buffer` that is passed into a `Blob`. Alternatively you can use the SDK for this as well using the [base64](/ipfs-sdk/upload/base64) method.
```typescript SDK {8}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh");
```
```typescript API {7-11}
const JWT = "YOUR_PINATA_JWT";
async function upload() {
try {
const formData = new FormData();
const buffer = Buffer.from("SGVsbG8gV29ybGQh", "base64");
const blob = new Blob([buffer]);
const file = new File([blob], "hello.txt");
formData.append("file", file);
const request = await fetch("https://uploads.pinata.cloud/v3/files", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`,
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Resumable Uploads
The Files API upload endpoint `https://uploads.pinata.cloud/v3/files` is fully [TUS](https://tus.io) compatible, so it can support larger files with the ability to resume uploads. Any file upload larger than 100MB needs to be uploaded through the TUS method. The [Files SDK](/sdk) handles this automatically when you use `pinata.upload.file()` by checking the file size before uploading.
```typescript {6}
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.file(massiveFile);
```
If you want to take advantage of resumable uploads then we would recommend using one of the [TUS clients](https://tus.io/implementations) and taking note of the following:
* Upload chunk size must be smaller than 50MB
* Instead of using the form data fields for `group_id` or `keyvalues` these can be passed directly into the upload metadata (see example below)
* Headers must include the Authorization with your [Pinata JWT](/account-managemnet/api-keys)
Here is an example of an upload to Pinata using the `tus-js-client`
```typescript
import * as tus from "tus-js-client";
async function resumeUpload(file) {
try {
const upload = new tus.Upload(file, {
endpoint: "https://uploads.pinata.cloud/v3/files",
chunkSize: 50 * 1024 * 1024, // 50MiB chunk size
retryDelays: [0, 3000, 5000, 10000, 20000],
onUploadUrlAvailable: async function () {
if (upload.url) {
console.log("Upload URL is available! URL: ", upload.url);
}
},
metadata: {
filename: "candyroad-demo.mp4", // name
filetype: "video/mp4",
group_id: "0192868e-6144-7685-9fc5-af68a1e48f29", // group ID
keyvalues: JSON.stringifiy({ env: "prod" }), // keyvalues
},
headers: { Authorization: `Bearer ${process.env.PINATA_JWT}` }, // auth header
uploadSize: fileStats.size,
onError: function (error) {
console.log("Failed because: " + error);
},
onProgress: function (bytesUploaded, bytesTotal) {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
console.log(percentage + "%");
},
onSuccess: function () {
console.log("Upload completed!");
},
});
upload.start();
} catch (error) {
console.log(error);
}
}
```
## Web App
You can also use the **[Pinata App](https://app.pinata.cloud/)** to upload files. It’s as simple as clicking the “Upload” button in the top right corner of the **[files page](https://app.pinata.cloud/pinmanager)**. 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](https://app.pinata.cloud/register)!
# Astro
Get started using Pinata with Astro
This guide will walk you through setting up Pinata with Astro
## Create an API Key and get Gateway URL
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started we recommend using Admin privileges, then move
to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
After you have your API key, you will want to get your Gateway domain. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway) see it listed there.
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# Next.js
Get started using Pinata with Next.js
This guide will walk you through setting up Pinata in a Next.js app.
## Create an API Key and get Gateway URL
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started we recommend using Admin privileges, then move
to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
After you have your API key, you will want to get your Gateway domain. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway) see it listed there.
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# Node.js
This Node.js quickstart should get you up and running with your standard backend javascript setup, and by using the API will give you flexibility when it comes to [uploading files](files/uploading-files).
## Generate your API Keys
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started, we recommend using Admin privileges, then
move to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# React
If you want to add Pinata to a React project that is client side only you can do so, however it must be stated that using your Pinata API keys in a client side app means they will be exposed! This approach is not secure and it is recommneded to either scope the API keys to certain permissions or use [signed JWTs](https://www.pinata.cloud/blog/how-to-upload-to-ipfs-from-the-frontend-with-signed-jwts) from a server.
It is still highly recommend using the [Next.js Quickstart](/quickstart/next-js) as it is much more secure with server side API routes and works similar to React.
**Using Pinata API keys in a React app will expose them, proceed with
caution!**
## Installation
### Create an API Key and get Gateway URL
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
We highly encourage scoping keys if you are planning to expose them in a client side environment
Once you have created the keys, you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, so be sure to copy them somewhere safe!
After you have your API key, you will want to get your Gateway domain. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway) see it listed there.
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# Remix
Get started using Pinata with Remix
This guide will walk you through setting up Pinata in a Remix app.
## Create an API Key and get Gateway URL
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started we recommend using Admin privileges, then move
to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
After you have your API key, you will want to get your Gateway domain. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway) see it listed there.
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# SvelteKit
This guide will walk you through setting up Pinata in a SvelteKit app.
## Create an API Key and get Gateway URL
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started we recommend using Admin privileges, then move
to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
After you have your API key, you will want to get your Gateway domain. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway) see it listed there.
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Select Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# Image Optimizations
Pinata image optimizations provides image optimization functionality directly through your [Gateway](/gateways/retrieving-files). These capabilities that can significantly improve the load time and experience when viewing image content.
## Usage
To use image optimizations on files you've uploaded simply add on the queries listed in the [reference](#reference) to your full Gateway url, which might look something like this:
```
https://example-gateway.mypinata.cloud/files/{cid}?img-width=500&img-height=500
```
There are differences when using image optmizations for public files vs private files, so be sure to visit the [retrieving files](/gateways/retrieving-files) for more information!
## Reference
At least one option must be specified. Options are comma-separated (spaces are not allowed anywhere). Names of options can be specified in full or abbreviated.
Specifies maximum width of the image in pixels. Exact behavior depends on the fit mode (described below).
Specifies maximum height of the image in pixels. Exact behavior depends on the fit mode (described below).
Device Pixel Ratio. Default 1. Multiplier for width/height that makes it easier to specify higher-DPI sizes in .
Affects interpretation of width and height. All resizing modes preserve aspect ratio. Available modes are:
* `img-fit=scale-down` Image will be shrunk in size to fully fit within the given width or height, but won’t be enlarged.
* `img-fit=contain` Image will be resized (shrunk or enlarged) to be as large as possible within the given width or height while preserving the aspect ratio.
* `img-fit=cover` Image will be resized to exactly fill the entire area specified by width and height, and will cropped if necessary.
* `img-fit=crop` Image will be shrunk and cropped to fit within the area specified by width and height. The image won’t be enlarged. For images smaller than the given dimensions it’s the same as scale-down. For images larger than the given dimensions, it’s the same as cover.
* `img-fit=pad` Image will be resized (shrunk or enlarged) to be as large as possible within the given width or height while preserving the aspect ratio, and the extra area will be filled with a background color (white by default). Transparent background may be very expensive, and it’s better to use fit=contain and CSS object-fit: contain property instead.
When cropping with `fit=cover`, specifies the most important side or point in the image that shouldn’t be cropped off.
* `img-gravity=auto` The point will be guessed by looking for areas that stand out the most from image background
* `img-gravity=side` and `img-gravity=XxY`
If a side (left, right, top, bottom) or coordinates specified on a scale from 0.0 (top or left) to 1.0 (bottom or right), 0.5 being the center. The X and Y coordinates are separated by lowercase x, e.g. 0x1 means left and bottom, 0.5x0.5 is the center, 0.5x0.33 is a point in the top third of the image.
Specifies quality for images in JPEG, WebP and AVIF formats. The quality is in 1-100 scale, but useful values are between 50 (low quality, small file size) and 90 (high quality, large file size). 85 is the default. When using the PNG format, an explicit quality setting allows use of PNG8 (palette) variant of the format.
Allows serving of the WebP format to browsers that support it. If this option is not specified, a standard format like JPEG or PNG will be used.
Reduces animations to still images. This setting is recommended to avoid surprisingly large animGIF files, or flashing images.
Specifies strength of sharpening filter. The value is a floating-point number between 0 (no sharpening) and 10 (max). 1 is a recommended value.
In case of a fatal error that prevents the image from being resized use `img-onerror=redirect` to redirect to the unresized source image URL. This may be useful in case some images require user authentication and cannot be fetched. This option shouldn’t be used if the source images may be very large. This option is ignored if the image is from another domain (subdomains are OK).
* `img-onerror=redirect` Redirects to original source url
Controls amount of invisible metadata (EXIF data) that should be preserved. Color profiles and EXIF rotation are applied to the image even if the metadata is discarded. Note that if the Polish feature is enabled, all metadata may have been removed already and this option may have no effect.
* `img-metadata=keep` Preserve most of the image metadata (including GPS location) when possible.
* `img-metadata=copyright` Discard all metadata except EXIF copyright tag. This is the default for JPEG images. img-metadata=none Discard all invisible metadata.
## Formats and limitations
Read JPEG, PNG, GIF (including animations), and WebP images. SVG is not supported, since this format is inherently scalable and does not need resizing. Resize and generate JPEG and PNG images, and optionally AVIF or WebP. AVIF format is supported on a best-effort basis. Images that cannot be compressed as AVIF will be served as WebP instead.
## SDK Method Reference
Image Optimizations are built into both [`get`](/sdk/gateways/get) and [`createdSignedURL`](/sdk/gateways/created-signed-url) methods of the SDK.
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
```typescript
const data = await pinata.gateways
.get("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
.optimizeImage({
width: 500,
height: 500,
format: "webp"
})
```
# Retrieving Files
Gateways are Pinata's tool to deliver your content with speed and security. They're similar to CDNs but with way more features. When you create a Pinata account, you'll automatically have a Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway).
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Retrieving Private Files
All content uploaded to Pinata is by default private, and there are a few ways you can view it. One of the simplest ways to fetch content is through the [get](/ipfs-sdk/gateways/get) method in the [SDK](/sdk). All content is referenced by the `cid`, a special identifier given to each file based on the cotnent of that file.
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const { data, contentType } = await pinata.gateways.get(
"bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq"
)
```
Under the hood this method generates a 30 second signed url to access the content with. Alternatively you can also create a signed URL that can be used to access the content for a specified limited amount of time.
```typescript SDK {8-11}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30, // Number of seconds link is valid for
});
```
```typescript API {5-10}
const JWT = "YOUR_PINATA_JWT";
async function url() {
try {
const payload = JSON.stringify({
url: "https://example.mypinata.cloud/files/bafybeifq444z4b7yqzcyz4a5gspb2rpyfcdxp3mrfpigmllh52ld5tyzwm",
expires: 500000,
date: 1724875300,
method: "GET"
})
const request = await fetch(
`https://api.pinata.cloud/v3/files/sign`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Image Optmizations
In order ot use [Image Optmizations](/gateways/image-optimizations) with private files the query parameters must be part of the request of getting a signed url.
```typescript SDK {3-7}
const data = await pinata.gateways
.createSignedURL("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
.optimizeImage({
width: 500,
height: 500,
format: "webp"
})
```
```typescript API {6}
const JWT = "YOUR_PINATA_JWT";
async function url() {
try {
const payload = JSON.stringify({
url: "https://example.mypinata.cloud/files/bafybeifq444z4b7yqzcyz4a5gspb2rpyfcdxp3mrfpigmllh52ld5tyzwm?img-width=500&img-height=500&img-format=webp",
expires: 500000,
date: 1724875300,
method: "GET"
})
const request = await fetch(
`https://api.pinata.cloud/v3/files/sign`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${JWT}`,
},
body: payload
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
## Retrieving Public Files
If you uploaded or added files to a [public group](/files/file-groups#public-vs-private-groups) then you can access them with the methods mentioned previously, or just by appending the `cid` to the `gateway` url like so:
```
https://example-gateway.mypinata.cloud/files/{cid}
```
Since these files are public there is no need create a temporary url. At this point you can also use some handy queries such as `?filename=image.png`, `?download=true`.
### Image Optimizations
With public files you can simply append the [Image Optimizations](/gateways/image-optimizations) queries which would look something like this:
```
https://example-gateway.mypinata.cloud/files/{cid}?img-width=500&img-height=500
```
## Adding a Custom Domain
Pinata also allows you to create a custom domain for your Dedicated Gateway. Simply visit the [Gateways Page](https://app.pinata.cloud/gateway), click the menu button on the right side of your gateway, then click Add Custom Domain. You'll need to own the domain you want to use. When you enter your domain, you will be prompted to enter DNS information through your registrar.
# LLM Docs
The Pinata Docs have multiple ways they can be used in LLMs or AI empowered code editors like Cursor or Zed. Both of these options return raw text that can be pasted into the LLM or used in a prompt/docs feature for an editor.
## Smaller Context
If you are concerned about context tokens then you will want to use our hand crafted [AI Docs](https://ai-docs.pinata.cloud)
```bash
curl https://ai-docs.pinata.cloud
```
## Larger Context
Pinata's docs also offer larger contexts if you prefer it. The standard route will return URL routes which make it easy for LLMs to crawl.
```bash Routes
curl https://docs.pinata.cloud/llms.txt
```
If you prefer to pull all of the documentaiton you can use the route below.
```bash Full Docs
curl https://docs.pinata.cloud/llms-full.txt
```
# Quickstart
Start uploading and retrieving content in no time
## Getting Started with Pinata
Whether you're brand new or a seasoned developer, Pinata makes it simple to store and retrieve content with speed and security. All you need to kick off your journey is a [free Pinata account](https://app.pinata.cloud/register)!
}
>
Quickstart
}
>
Quickstart
}
>
Quickstart
}
>
Quickstart
}
>
Quickstart
}
>
Quickstart
### 1. Get API key and Gateway URL
Inside the [Pinata App](https://app.pinata.cloud) select "API Keys" from the sidebar, then click "New Key" in the top right. We would recommend starting with Admin privileges and unlimited uses to start. You will receive a `pinata_api_key`, `pinata_api_secret`, and a `JWT`. The JWT is the most common authentication method and what we'll be using below.
Next you will want to grab your Gateway domain by clicking the Gateways tab in the sidebar. You should see it listed in the format `fun-llama-300.mypinata.cloud` and you will want to copy it exactly like that.
### 2. Choose Your Setup
Use Pinata's first class Files API and SDK which uses private files by default
Use Pinata's renown IPFS API and SDK for Web3 apps
# bandwidth
Get analytics on bandwidth for multiple properties
## Usage
The `analytics` class is unique in that it very flexible, but also can require more queries to be used well. Be sure to real the [Parameters](#parameters) in detail to understand how it can be used.
The `bandwidth` method will sort results by highest number of bandwidth, but will also include `requests` values.
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const clicks = await pinata.analytics.bandwidth
.days(30)
.limit(10)
.cid("")
```
## Returns
What is returned in `value` will depend on they property or query used. For instance, using `cid()` will return CIDs, `country()` will return Countries, etc.
```typescript
type TopAnalyticsResponse = {
data: TopAnalyticsItem[];
};
type TopAnalyticsItem = {
value: string;
requests: number;
bandwidth: number;
};
```
## Parameters
Filter response with the following additional methods. It does require at least one property, such as `cid`, `fileName`, `userAgent`, `country`, `region`, or `referrer`.
### cid
* Type: `string`
Returns bandwidth for all CIDs
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.cid()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.cid("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
```
### fileName
* Type: `string`
Returns bandwidth for all file names
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.fileName()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.fileName("pinnie.png")
```
### userAgent
* Type: `string`
Returns bandwidth for user agents
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.userAgent()
```
Filter by passing an argument
```typescript {3-5}
const files = await pinata.analytics.bandwidth
.days(7)
.userAgent(
"Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0"
)
```
### country
* Type: `string`
Returns bandwidth for countries
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.country()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.country("us")
```
### region
* Type: `string`
Returns bandwidth for regions inside of countries
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.region()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.region("us - VA")
```
### referer
* Type: `string`
Returns bandwidth for referers
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.referer()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.bandwidth
.days(7)
.referer("https://docs.pinata.cloud/")
```
### days
* Type: `number`
Number of days to query. Starts with current date and then goes back by provided number.
```typescript {2}
const files = await pinata.analytics.bandwidth
.days(7)
.cid()
```
### sort
* Type: `"asc" | "desc"`
Order results either ascending or descending by created date
```typescript {4}
const files = await pinata.analytics.bandwidth
.days(7)
.cid()
.sort("asc")
```
### limit
* Type: `number`
Limit the number of results
```typescript {4}
const files = await pinata.analytics.bandwidth
.days(7)
.cid()
.limit(10)
```
### customDates
* Type: `string, string`
Custom dates to query using a start and end date with the format `YYYY-MM-DD`
```typescript {2}
const files = await pinata.analytics.bandwidth
.customDates("2024-11-01", "2024-11-20")
.cid()
```
### from
* Type: `string`
Provide an alternate Gateway domain to query instead of the default one found in the Pinata SDK Config
```typescript {4}
const files = await pinata.analytics.bandwidth
.days(7)
.cid()
.from("example-2.mypinata.cloud")
```
# requests
Get analytics on requests for multiple properties
## Usage
The `analytics` class is unique in that it very flexible, but also can require more queries to be used well. Be sure to real the [Parameters](#parameters) in detail to understand how it can be used.
The `requests` method will sort results by highest number of requests, but will also include `bandwidth` values.
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const clicks = await pinata.analytics.requests
.days(30)
.limit(10)
.cid("")
```
## Returns
What is returned in `value` will depend on they property or query used. For instance, using `cid()` will return CIDs, `country()` will return Countries, etc.
```typescript
type TopAnalyticsResponse = {
data: TopAnalyticsItem[];
};
type TopAnalyticsItem = {
value: string;
requests: number;
bandwidth: number;
};
```
## Parameters
Filter response with the following additional methods. It does require at least one property, such as `cid`, `fileName`, `userAgent`, `country`, `region`, or `referrer`.
### cid
* Type: `string`
Returns requests for all CIDs
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.cid()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.cid("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
```
### fileName
* Type: `string`
Returns requests for all file names
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.fileName()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.fileName("pinnie.png")
```
### userAgent
* Type: `string`
Returns requests for user agents
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.userAgent()
```
Filter by passing an argument
```typescript {3-5}
const files = await pinata.analytics.requests
.days(7)
.userAgent(
"Mozilla/5.0 (X11; Linux x86_64; rv:132.0) Gecko/20100101 Firefox/132.0"
)
```
### country
* Type: `string`
Returns requests for countries
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.country()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.country("us")
```
### region
* Type: `string`
Returns requests for regions inside of countries
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.region()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.region("us - VA")
```
### referer
* Type: `string`
Returns requests for referers
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.referer()
```
Filter by passing an argument
```typescript {3}
const files = await pinata.analytics.requests
.days(7)
.referer("https://docs.pinata.cloud/")
```
### days
* Type: `number`
Number of days to query. Starts with current date and then goes back by provided number.
```typescript {2}
const files = await pinata.analytics.requests
.days(7)
.cid()
```
### sort
* Type: `"asc" | "desc"`
Order results either ascending or descending by created date
```typescript {4}
const files = await pinata.analytics.requests
.days(7)
.cid()
.sort("asc")
```
### limit
* Type: `number`
Limit the number of results
```typescript {4}
const files = await pinata.analytics.requests
.days(7)
.cid()
.limit(10)
```
### customDates
* Type: `string, string`
Custom dates to query using a start and end date with the format `YYYY-MM-DD`
```typescript {2}
const files = await pinata.analytics.requests
.customDates("2024-11-01", "2024-11-20")
.cid()
```
### from
* Type: `string`
Provide an alternate Gateway domain to query instead of the default one found in the Pinata SDK Config
```typescript {4}
const files = await pinata.analytics.requests
.days(7)
.cid()
.from("example-2.mypinata.cloud")
```
# Config
Overview of the Files SDK Config
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
```
## Parameters
### pinataJwt
* Type: `string`
The Pinata API JWT key that authorizes the SDK. [Read more about API Keys](/account-management/api-keys).
### pinataGateway (Optional)
* Type: `string`
The domain of the Gateway included with your account. [Read more about Gateways](/gateways/retrieving-files).
# addSwap
Swap a CID for another using the [Hot Swaps](/gateways/plugins/hot-swaps) gateway plugin
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const swap = await pinata.files.addSwap({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
## Returns
```typescript
export type SwapCidResponse = {
mapped_cid: string;
created_at: string;
};
```
## Parameters
Pass in the required parameters below to swap a CID
### cid
* Type: `string`
This would be the original CID that would be visted
```typescript {2}
const swap = await pinata.files.addSwap({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
### swapCid
* Type: `string`
This would be the CID you would want the old CID to point to
```typescript {3}
const swap = await pinata.files.addSwap({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
# delete
Delte an array of files from your account
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const deletedFiles = await pinata.files.delete([
"4ad9d3d1-4ab4-464c-a42a-3027fc39a546"
])
```
## Returns
```typescript
type DeleteResponse[] = {
id: string;
status: string;
};
```
## Parameters
### files
* Type: `string[]`
An array of file IDs you want to delete
```typescript
const unpin = await pinata.files
.delete([
"5e3011c0-f242-46b8-ad8d-2141bba23096",
"e4cb100d-9065-4a08-80a3-f195f35de336"
])
```
# deleteSwap
Remove a CID swap for [Hot Swaps](/gateways/plugins/how-swaps) plugin
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const deleteSwap = await pinata.files.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
```
## Returns
```typescript
OK
```
## Parameters
Pass in the required parameters below to remove a CID swap
### cid
* Type: `string`
This would be the original CID that was swapped
```typescript
const deleteSwap = await pinata.files.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
```
# getSwapHistory
See the history of [Hot Swaps](/gateways/plugins/hot-swaps) on a Gateway domain for a specified CID.
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const history = await pinata.files.getSwapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
```
## Returns
```typescript
SwapCidResponse[]
export type SwapCidResponse = {
mapped_cid: string;
created_at: string;
};
```
## Parameters
Pass in the required parameters to get a swap history
### cid
* Type: `string`
The target CID for swap history
```typescript {2}
const history = await pinata.files.getSwapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
```
### domain
* Type: `string`
The Gateway domain that has the Hot Swaps plugin installed
```typescript {3}
const history = await pinata.files.getSwapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
```
# list
List and filter files pinned to your Pinata account
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const files = await pinata.files.list()
```
## Returns
```typescript
type FileListResponse = {
files: FileListItem[];
next_page_token: string;
};
type FileListItem = {
id: string;
name: string | null;
cid: "pending" | string;
size: number;
numberOfFiles: number;
mimeType: string;
groupId: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Filter results based on name
```typescript {3}
const files = await pinata.files
.list()
.name("pinnie")
```
### group
* Type: `string`
Filter results based on group ID
```typescript {3}
const files = await pinata.files
.list()
.group("5b56981c-7e5b-4dff-aeca-de784728dddb")
```
### noGroup
* Type: `boolean`
Filter results to only show files that are not part of a group
```typescript {3}
const files = await pinata.files
.list()
.noGroup(true)
```
### cid
* Type: `string`
Filter results based on CID
```typescript {3}
const files = await pinata.files
.list()
.cid("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
```
### metadata
* Type: `Record`
Filter results based on keyvalue pairs in metadata
```typescript {3-5}
const files = await pinata.files
.list()
.metadata({
env: "prod"
})
```
### mimeType
* Type: `string`
Filter results based on mime type
```typescript {3}
const files = await pinata.files
.list()
.mimeType("image/png")
```
### order
* Type: `"ASC" | "DESC"`
Order results either ascending or descending by created date
```typescript {3}
const files = await pinata.files
.list()
.order("ASC")
```
### limit
* Type: `number`
Limit the number of results
```typescript {3}
const files = await pinata.files
.list()
.limit(10)
```
### cidPending
* Type: `boolean`
Filters results and only returns files where `cid` is still `pending`
```typescript {3}
const files = await pinata.files
.list()
.cid(true)
```
### pageToken
* Type: `string`
Paginates through files based on a provided page token
```typescript {3}
const files = await pinata.files
.list()
.pageToken("MDE5MWIzZWYtM2U0Zi03YTY5LWE3OTQtOTRhZDE5NjQxMTk0")
```
## Auto Paginate
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.files.list()) {
console.log(item.id);
}
```
Works like magic ✨
# update
Update information for an existing file
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example.mypinata.cloud",
});
const update = await pinata.files.update({
id: "52681e41-86f4-407b-8f79-33a7e7e5df68",
name: "New File Name",
})
```
## Returns
```typescript
type FileListItem = {
id: string;
name: string | null;
cid: "pending" | string;
size: number;
numberOfFiles: number;
mimeType: string;
groupId: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
### id
* Type: `string`
ID of the target file to update
```typescript {2}
const update = await pinata.files.update({
id: "8809812b-cd36-499f-b9b3-a37258a9cd6a",
name: "New File Name",
})
```
### name (Optional)
* Type: `string`
Update the name of a file
```typescript {3}
const update = await pinata.files.update({
id: "8809812b-cd36-499f-b9b3-a37258a9cd6a",
name: "New File Name"
})
```
# createSignedURL
Create a Signed URL for a file
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30,
});
```
## Returns
* Type: `string`
The full signed URL
```
https://example-gateway.mypinata.cloud/files/bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq?X-Algorithm=PINATA1&X-Date=1724943397&X-Expires=30&X-Method=GET&X-Signature=
```
## Parameters
### cid
* Type: `string`
Accepts CID of the file you are trying to create a signed URL for
```typescript {2}
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30,
});
```
### expires
* Type: `number`
The number of seconds the signed URL should be valid for
```typescript {3}
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30,
});
```
### date (Optional)
* Type: `number`
A UNIX timestamp of the date a URL is signed
```typescript {1-2,7}
const date = Math.floor(new Date().getTime() / 1000);
//date: 1724943711
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30,
date: date
});
```
### gateway (Optional)
* Type: `string`
Use a Gateway domain other than the default domain from the config
```typescript {4}
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 30,
gateway: "discordpinnie.mypinata.cloud"
});
```
### optimizeImage (Optional)
* Type: [OptimizeImageOptions](/sdk/types#optimizeimageoptions)
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
If the content being fetched is an image you can apply image optimizations to the image.
```typescript {3-7}
const data = await pinata.gateways
.createSignedURL("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
.optimizeImage({
width: 500,
height: 500,
format: "webp"
})
```
# get
Retrieve a file through the config's `pinataGateway`
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = await pinata.gateways.get("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
## Returns
Returns the data in the form of `JSON`, `string`, or `Blob` as well as the `ContentType`
```typescript
type GetCIDResponse = {
data?: JSON | string | Blob | null;
contentType: ContentType;
};
type ContentType =
| "application/json"
| "application/xml"
| "text/plain"
| "text/html"
| "text/css"
| "text/javascript"
| "application/javascript"
| "image/jpeg"
| "image/png"
| "image/gif"
| "image/svg+xml"
| "audio/mpeg"
| "audio/ogg"
| "video/mp4"
| "application/pdf"
| "application/octet-stream"
| string;
```
## Parameters
### cid
* Type: `string`
Accepts CID of the file you are trying to fetch
```typescript
const data = await pinata.gateways.get(
"bafybeibo5zcqeorhqxczodrx52rn7byyrwfvwthz5dspnjlbkd7zkugefi",
);
```
### optimizeImage (Optional)
* Type: [OptimizeImageOptions](/sdk/types#optimizeimageoptions)
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
If the content being fetched is an image you can apply image optimizations to the image.
```typescript {3-7}
const data = await pinata.gateways
.get("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4")
.optimizeImage({
width: 500,
height: 500,
format: "webp"
})
```
# Getting Started
Get up and running with the Files SDK
The Files SDK is an all-in-one tool for everything you might need, from uploading content, using Gateways, even user or group management!
Looking for our IPFS SDK? You can find it [here](/web3/sdk)!
## 1. Installation and Setup
Install with your package manager of choice
```bash npm
npm i pinata
```
```bash pnpm
pnpm i pinata
```
```bash yarn
yarn add pinata
```
```bash bun
bun i pinata
```
Import and initialize the SDK in your codebase with the following variables
* [Pinata API Key JWT](/account-management/api-keys)
* [Pinata Dedicated Gateway Domain](/gateways/retrieving-files)
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: "PINATA_JWT",
pinataGateway: "example-gateway.mypinata.cloud",
});
```
The `PINATA_JWT` is a secret key, be sure to initialize the SDK in a secure environment and practice basic variable security practices. If you need to upload from a client environment, consider using signed JWTs
## 2. Upload a File
```typescript {11}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
async function main() {
try {
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
console.log(upload);
} catch (error) {
console.log(error);
}
}
await main();
```
This will return an object like the following
```typescript
{
id: "41bc6820-43d3-46f8-a9e0-e53e8adea2e8",
name: "hello.txt",
cid: "bafkreid7qoywk77r7rj3slobqfekdvs57qwuwh5d2z3sqsw52iabe3mqne",
size: 12,
number_of_files: 1,
mime_type: "text/plain;charset=utf-8",
user_id: "cb7c7bd8-7deb-479d-8750-34a20f2adb3b",
group_id: null,
}
```
## 3. Retrieve a File
Use the `cid` of a file to fetch it through a Gateway, or create a signed URL.
```typescript {13-16}
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
async function main() {
try {
const data = await pinata.gateways.get("bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq");
console.log(data)
const url = await pinata.gateways.createSignedURL({
cid: "bafkreib4pqtikzdjlj4zigobmd63lig7u6oxlug24snlr6atjlmlza45dq",
expires: 1800,
})
console.log(url)
} catch (error) {
console.log(error);
}
}
main();
```
# addFiles
Add Files to a Group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.addFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
## Returns
```typescript
type UpdateGroupFilesResponse[] = {
id: string;
status: string;
};
```
## Parameters
### groupId
* Type: `string`
ID of the target Group to add files to
```typescript {2}
const group = await pinata.groups.addFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
### files
* Type: `string[]`
An array of file IDs as strings that you want to add to the group
```typescript {3-6}
const group = await pinata.groups.addFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
# create
Create a Group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.create({
name: "My New Group",
isPublic: true
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
is_public: boolean;
name: string;
created_at: string;
};
```
## Parameters
### name
* Type: `string`
Requires a name for the group to be created
```typescript {2}
const group = await pinata.groups.create({
name: "My New Group",
});
```
### isPublic
\-type: `boolean`
Determine whether or not a group is public upon creation. Default is `false`.
```typescript {3}
const group = await pinata.groups.create({
name: "My New Group",
isPublic: true
});
```
# delete
Delete a Group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
## Returns
```typescript
OK
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript {2}
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
# get
Get info for an existing group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
is_public: boolean;
name: string;
created_at: string;
};
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript {2}
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
# list
List and filter through all Groups
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups
.list()
```
## Returns
```typescript
type GroupListResponse = {
groups: GroupResponseItem[];
next_page_token: string;
};
type GroupResponseItem = {
id: string;
is_public: boolean;
name: string;
created_at: string;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Filters groups based the group name
```typescript {3}
const groups = await pinata.groups
.list()
.name("SDK")
```
### isPublic
* Type: `boolean`
Filters groups based on whether they are public or not
```typescript {3}
const groups = await pinata.groups
.list()
.isPublic(true)
```
### limit
* Type: `number`
Limits the number of results
```typescript {3}
const groups = await pinata.groups
.list()
.limit(10)
```
### pageToken
* Type: `string`
Paginates through groups based on a provided page token
```typescript {3}
const groups = await pinata.groups
.list()
.pageToken("MDE5MWIzZWYtM2U0Zi03YTY5LWE3OTQtOTRhZDE5NjQxMTk0")
```
## Auto Paginate
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.gateways.list()) {
console.log(item.name);
}
```
# removeFiles
Remove Files from a Group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.removeFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
## Returns
```typescript
type UpdateGroupFilesResponse[] = {
id: string;
status: string;
};
```
## Parameters
### groupId
* Type: `string`
ID of the target Group to remove files from
```typescript {2}
const group = await pinata.groups.removeFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
### files
* Type: `string[]`
An array of file IDs as strings that you want to remove from the group
```typescript {3-6}
const group = await pinata.groups.removeFiles({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
files: [
"7e18c4a4-9501-44de-8f81-403db7de0e39",
"a606ef7e-70a0-40ad-9b8a-60563e009655"
],
});
```
# update
Update the name of a group
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
name: "My New Group 2",
public: true
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
is_public: boolean;
name: string;
created_at: string;
};
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript {2}
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
### name (Optional)
* Type: `string`
Updated name for the target group
```typescript {2}
const groups = await pinata.groups.update({
name: "My Group Again",
});
```
### isPublic (Optional)
* Type: `boolean`
Update a group to be public or private
```typescript {2}
const groups = await pinata.groups.update({
isPublic: true
});
```
# create
Create an API key
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
},
maxUses: 1,
});
```
## Returns
```typescript
type KeyResponse = {
JWT: string;
pinata_api_key: string;
pinata_api_secret: string;
};
```
## Parameters
### keyName
* Type: `string`
Name for the API key
```typescript {2}
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
},
maxUses: 1,
});
```
### maxUses (Optional)
* Type: `number`
Limit the number of uses a key is valid for
```typescript {6}
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
},
maxUses: 1,
});
```
### permissions
* Type: [KeyPermissions](../types#keypermissions)
#### admin (Optional)
* Type: `boolean`
Grants the key admin access to all endpoints
```typescript {4}
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
},
maxUses: 1,
});
```
#### endpoints (Optional)
* Type [Endpoints](../types#endpoints)
```typescript {3-20}
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
endpoints: {
data: {
pinList: true,
userPinnedDataTotal: false
},
pinning: {
hashMetadata: true,
hashPinPolicy: false,
pinByHash: true,
pinFileToIPFS: true,
pinJSONToIPFS: true,
pinJobs: false,
unpin: false,
userPinPolicy: false
}
}
}
});
```
# list
List and filter through Keys
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const keys = await pinata.keys
.list()
.name("Admin")
.revoked(false)
```
## Returns
```typescript
type KeyListItem = {
id: string;
name: string;
key: string;
secret: string;
max_uses: number;
uses: number;
user_id: string;
scopes: KeyScopes;
revoked: boolean;
createdAt: string;
updatedAt: string;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Filter by name, uses "contains" matching
```typescript
const keys = await pinata.keys
.list()
.name("Greetings");
```
### revoked
* Type: `boolean`
Filter keys by whether or not they have been revoked
```typescript
const keys = await pinata.keys
.list()
.revoked(false);
```
### exhausted
* Type: `boolean`
Filter keys based on whether they had limited uses that were exhuasted
```typescript
const keys = await pinata.keys
.list()
.exhausted(false);
```
### offset
* Type: `number`
Offset the number of keys returned to paginate
```typescript
const keys = await pinata.keys
.list()
.offset(5);
```
## Auto Paginate
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.keys.list()) {
console.log(item.name);
}
```
# revoke
Revoke an API Key
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const revoke = await pinata.keys.revoke([
"94566af5e63833e260be"
]);
```
## Returns
```typescript
type RevokeKeyResponse[] = {
key: string;
status: string;
};
```
## Parameters
### keys
* Type: `string[]`
An array of API Keys to revoke. This is the `key` found in the response of `keys.list`
```typescript
const revoke = await pinata.keys.revoke([
"94566af5e63833e260be"
]);
```
# testAuthenticaiton
Tests authentication with the current `PINATA_JWT`
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const auth = await pinata.testAuthentication()
```
## Returns
```typescript
type AuthTestResponse = {
message: string;
};
```
# null
## `AuthTestResponse`
```typescript
type AuthTestResponse = {
message: string;
};
```
## `ContainsCIDResponse`
```typescript
type ContainsCIDResponse = {
containsCid: boolean;
cid: string | null;
};
```
## `ContentType`
```typescript
type ContentType =
| "application/json"
| "application/xml"
| "text/plain"
| "text/html"
| "text/css"
| "text/javascript"
| "application/javascript"
| "image/jpeg"
| "image/png"
| "image/gif"
| "image/svg+xml"
| "audio/mpeg"
| "audio/ogg"
| "video/mp4"
| "application/pdf"
| "application/octet-stream"
| string
| null;
```
## `DataEndpoints`
```typescript
type DataEndpoints = {
pinList?: boolean;
userPinnedDataTotal?: boolean;
};
```
## `DeleteResponse`
```typescript
type DeleteResponse = {
id: string;
status: string;
};
```
## `Endpoints`
```typescript
type Endpoints = {
data?: DataEndpoints;
pinning?: PinningEndpoints;
};
```
## `FileListItem`
```typescript
type FileListItem = {
id: string;
name: string | null;
cid: "pending" | string;
size: number;
number_of_files: number;
mime_type: string;
group_id: string | null;
created_at: string;
};
```
## `FileListQuery`
```typescript
type FileListQuery = {
limit?: number;
pageToken?: string;
cidPending?: boolean;
};
```
## `FileListResponse`
```typescript
type FileListResponse = {
files: FileListItem[];
next_page_token: string;
};
```
## `FileObject`
```typescript
type FileObject = {
name: string;
size: number;
type: string;
lastModified: number;
arrayBuffer: () => Promise;
};
```
## `GatewayAnalyticsQuery`
```typescript
type GatewayAnalyticsQuery = {
gateway_domain: string;
start_date: string;
end_date: string;
cid?: string;
file_name?: string;
user_agent?: string;
country?: string;
region?: string;
referer?: string;
limit?: number;
sort_order?: "asc" | "desc";
};
```
## `GetCIDResponse`
```typescript
type GetCIDResponse = {
data?: JSON | string | Blob | null;
contentType: ContentType;
};
```
## `GetGroupOptions`
```typescript
type GetGroupOptions = {
groupId: string;
};
```
## `GroupCIDOptions`
```typescript
type GroupCIDOptions = {
groupId: string;
cids: string[];
};
```
## `GroupListResponse`
```typescript
type GroupListResponse = {
groups: GroupResponseItem[];
next_page_token: string;
};
```
## `GroupOptions`
```typescript
type GroupOptions = {
name: string;
isPublic?: boolean;
};
```
## `GroupQueryOptions`
```typescript
type GroupQueryOptions = {
nameContains?: string;
limit?: number;
pageToken?: string;
isPublic?: boolean;
};
```
## `GroupResponseItem`
```typescript
type GroupResponseItem = {
id: string;
is_public: boolean;
name: string;
createdAt: string;
};
```
## `JsonBody`
```typescript
type JsonBody = Record;
```
## `KeyListItem`
```typescript
type KeyListItem = {
id: string;
name: string;
key: string;
secret: string;
max_uses: number;
uses: number;
user_id: string;
scopes: KeyScopes;
revoked: boolean;
createdAt: string;
updatedAt: string;
};
```
## `KeyListQuery`
```typescript
type KeyListQuery = {
revoked?: boolean;
limitedUse?: boolean;
exhausted?: boolean;
name?: string;
offset?: number;
};
```
## `KeyListResponse`
```typescript
type KeyListResponse = {
keys: KeyListItem[];
count: number;
};
```
## `KeyOptions`
```typescript
type KeyOptions = {
keyName: string;
permissions: KeyPermissions;
maxUses?: number;
};
```
## `KeyPermissions`
```typescript
type KeyPermissions = {
admin?: boolean;
endpoints?: Endpoints;
};
```
## `KeyResponse`
```typescript
type KeyResponse = {
JWT: string;
pinata_api_key: string;
pinata_api_secret: string;
};
```
## `KeyScopes`
```typescript
type KeyScopes = {
endpoints: {
pinning: {
pinFileToIPFS: boolean;
pinJSONToIPFS: boolean;
};
};
admin: boolean;
};
```
## `OptimizeImageOptions`
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
## `PinataConfig`
```typescript
type PinataConfig = {
pinataJwt: string | undefined;
pinataGateway?: string;
pinataGatewayKey?: string;
customHeaders?: Record;
endpointUrl?: string;
uploadUrl?: string;
};
```
## `PinataMetadata`
```typescript
type PinataMetadata = {
name?: string;
keyvalues: Record;
};
```
## `PinningEndpoints`
```typescript
type PinningEndpoints = {
hashMetadata?: boolean;
hashPinPolicy?: boolean;
pinByHash?: boolean;
pinFileToIPFS?: boolean;
pinJSONToIPFS?: boolean;
pinJobs?: boolean;
unpin?: boolean;
userPinPolicy?: boolean;
};
```
## `RevokeKeyResponse`
```typescript
type RevokeKeyResponse = {
key: string;
status: string;
};
```
## `SignatureOptions`
```typescript
type SignatureOptions = {
cid: string;
signature: string;
};
```
## `SignatureResponse`
```typescript
type SignatureResponse = {
cid: string;
signature: string;
};
```
## `SignedUrlOptions`
```typescript
type SignedUrlOptions = {
cid: string;
date?: number;
expires: number;
};
```
## `SwapCidOptions`
```typescript
type SwapCidOptions = {
cid: string;
swapCid: string;
};
```
## `SwapCidResponse`
```typescript
type SwapCidResponse = {
mappedCid: string;
createdAt: string;
};
```
## `SwapHistoryOptions`
```typescript
type SwapHistoryOptions = {
cid: string;
domain: string;
};
```
## `TimeIntervalGatewayAnalyticsQuery`
```typescript
type TimeIntervalGatewayAnalyticsQuery = GatewayAnalyticsQuery & {
sort_by?: "requests" | "bandwidth";
date_interval: "day" | "week";
};
```
## `TimeIntervalGatewayAnalyticsResponse`
```typescript
type TimeIntervalGatewayAnalyticsResponse = {
total_requests: number;
total_bandwidth: number;
time_periods: TimePeriodItem[];
};
```
## `TimePeriodItem`
```typescript
type TimePeriodItem = {
period_start_time: string;
requests: number;
bandwidth: number;
};
```
## `TopGatewayAnalyticsItem`
```typescript
type TopGatewayAnalyticsItem = {
value: string;
requests: number;
bandwidth: number;
};
```
## `TopGatewayAnalyticsQuery`
```typescript
type TopGatewayAnalyticsQuery = GatewayAnalyticsQuery & {
sort_by: "requests" | "bandwidth";
attribute:
| "cid"
| "country"
| "region"
| "user_agent"
| "referer"
| "file_name";
};
```
## `UpdateFileOptions`
```typescript
type UpdateFileOptions = {
id: string;
name?: string;
};
```
## `UpdateGroupOptions`
```typescript
type UpdateGroupOptions = {
groupId: string;
name?: string;
isPublic?: boolean;
};
```
## `UploadOptions`
```typescript
type UploadOptions = {
metadata?: PinataMetadata;
keys?: string;
groupId?: string;
};
```
## `UploadResponse`
```typescript
type UploadResponse = {
id: string;
name: string;
cid: string;
size: number;
created_at: string;
number_of_files: number;
mime_type: string;
user_id: string;
group_id: string | null;
is_duplicate: boolean | null;
};
```
## `UserPinnedDataResponse`
```typescript
type UserPinnedDataResponse = {
pin_count: number;
pin_size_total: number;
pin_size_with_replications_total: number;
};
```
# base64
Upload a base64 string to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh")
```
## Returns
```typescript
type UploadResponse = {
id: string;
name: string;
cid: string;
size: number;
number_of_files: number;
mime_type: string;
user_id: string;
};
```
## Parameters
### base64
* Type: `string`
Accepts a string encoded in base64
```typescript
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh")
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript {3}
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript {3-8}
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.addMetadata({
name: "hello.txt",
keyvalues: {
env: "prod"
}
})
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript {3}
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.key("GENERATED_API_JWT")
```
# file
Upload a single file to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const upload = await pinata.upload.file(file)
```
### Local Files
If you need to upload files from a local file source you can use `fs` to feed a file into a `blob`, then turn that `blob` into a `File`.
```typescript {10-12}
const { PinataSDK } = require("pinata")
const fs = require("fs")
const { Blob } = require("buffer")
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud"
})
const blob = new Blob([fs.readFileSync("./hello-world.txt")]);
const file = new File([blob], "hello-world.txt", { type: "text/plain"})
const upload = await pinata.upload.file(file);
```
## Returns
```typescript
type UploadResponse = {
id: string;
name: string;
cid: string;
size: number;
number_of_files: number;
mime_type: string;
user_id: string;
};
```
## Parameters
### file
* Type: `File` object
Accepts a File object in accordance with the [W3C File API](https://w3c.github.io/FileAPI/#file-section).
```typescript {3}
const blob = new Blob(["hello world!"], { type: "text/plain" })
const file = new File([blob], "hello.txt", { type: "text/plain" })
const upload = await pinata.upload.file(file)
```
In most environments you can also pass a Blob here as well.
```typescript {2}
const blob = new Blob(["hello world!"], { type: "text/plain" })
const upload = await pinata.upload.file(blob)
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript {3}
const upload = await pinata.upload
.file(file)
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript {3-8}
const upload = await pinata.upload
.file(file)
.addMetadata({
name: "hello.txt",
keyvalues: {
env: "prod"
}
})
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript {3}
const upload = await pinata.upload
.file(file)
.key("GENERATED_API_JWT")
```
# json
Upload a JSON object to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.json({
content: "console.log('hello world!)",
name: "helloworld.ts",
lang: "ts"
})
```
## Returns
```typescript
type UploadResponse = {
id: string;
name: string;
cid: string;
size: number;
number_of_files: number;
mime_type: string;
user_id: string;
};
```
## Parameters
### JSON
* Type: `Record`
Accepts an object that is turned into JSON
```typescript
const upload = await pinata.upload.json({
content: "console.log('hello world!)",
name: "helloworld.ts",
lang: "ts"
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript {7}
const upload = await pinata.upload
.json({
content: "console.log('hello world!)",
name: "helloworld.ts",
lang: "ts"
})
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript {7-12}
const upload = await pinata.upload
.json({
content: "console.log('hello world!)",
name: "helloworld.ts",
lang: "ts"
})
.addMetadata({
name: "hello.json",
keyvalues: {
env: "prod"
}
})
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript {7}
const upload = await pinata.upload
.json({
content: "console.log('hello world!)",
name: "helloworld.ts",
lang: "ts"
})
.key("GENERATED_API_JWT")
```
# url
Upload the contents of a URL to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif")
```
## Returns
```typescript
type UploadResponse = {
id: string;
name: string;
cid: string;
size: number;
number_of_files: number;
mime_type: string;
user_id: string;
};
```
## Parameters
### url
* Type: `string`
Accepts a URL in the form of a string. The mimetype of the URL body provided in the headers typically determines the resulting file.
```typescript
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif")
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript {3}
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript {3-8}
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.addMetadata({
name: "pinnie.gif",
keyvalues: {
env: "prod"
}
})
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript {3}
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.key("GENERATED_API_JWT")
```
# Files CLI
The official CLI for the Files API written in Go
![cover](https://dweb.mypinata.cloud/ipfs/QmNcdx9t48z7RQUXUZZHmuc4zBfyBxKLjDfEgmfhiop7j7?img-format=webp)
The Files CLI is a tool you can use alongside your account to upload and manage files through your terminal. The source code can be found in the link below.
## Installation
If you are on Windows please use WSL when installing. If you get an error that it was not able to resolve the github host run `git config --global --unset http.proxy`
### Install Script
The easiest way to install is to copy and paste this script into your terminal
```bash
curl -fsSL https://cli.pinata.cloud/install | bash
```
### Homebrew
If you are on MacOS and have homebrew installed you can run the command below to install the CLI
```
brew install PinataCloud/files-cli/files-cli
```
### Building from Source
To build and instal from source make sure you have [Go](https://go.dev/) installed on your computer and the following command returns a version:
```
go version
```
Then paste and run the following into your terminal:
```
git clone https://github.com/PinataCloud/files-cli && cd files-cli && go install .
```
### Linux Binary
As versions are released you can visit the [Releases](https://github.com/PinataCloud/files-cli/releases) page and download the appropriate binary for your system, them move it into your bin folder.
For example, this is how I install the CLI for my Raspberry Pi
```
wget https://github.com/PinataCloud/files-cli/releases/download/v0.1.0/files-cli_Linux_arm64.tar.gz
tar -xzf files-cli_Linux_arm64.tar.gz
sudo mv pinata /usr/bin
```
## Usage
The Pinata CLI is equipped with the majortiry of features on the Files API.
### `auth`
With the CLI installed you will first need to authenticate it with your [Pinata JWT](https://docs.pinata.cloud/account-management/api-keys). Run this command and follow the steps to setup the CLI!
```
pinata auth
```
### `upload`
```
NAME:
pinata upload - Upload a file to Pinata
USAGE:
pinata upload [command options] [path to file]
OPTIONS:
--group value, -g value Upload a file to a specific group by passing in the groupId
--name value, -n value Add a name for the file you are uploading. By default it will use the filename on your system. (default: "nil")
--verbose Show upload progress (default: false)
--help, -h show help
```
### `files`
```
NAME:
pinata files - Interact with your files on Pinata
USAGE:
pinata files command [command options] [arguments...]
COMMANDS:
delete, d Delete a file by ID
get, g Get file info by ID
update, u Update a file by ID
list, l List most recent files
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help
```
#### `get`
```
NAME:
pinata files get - Get file info by ID
USAGE:
pinata files get [command options] [ID of file]
OPTIONS:
--help, -h show help
```
#### `list`
```
NAME:
pinata files list - List most recent files
USAGE:
pinata files list [command options] [arguments...]
OPTIONS:
--name value, -n value Filter by name of the target file
--cid value, -c value Filter results by CID
--group value, -g value Filter results by group ID
--mime value, -m value Filter results by file mime type
--amount value, -a value The number of files you would like to return
--token value, -t value Paginate through file results using the pageToken
--cidPending Filter results based on whether or not the CID is pending (default: false)
--keyvalues value, --kv value [ --keyvalues value, --kv value ] Filter results by metadata keyvalues (format: key=value)
--help, -h show help
```
#### `update`
```
NAME:
pinata files update - Update a file by ID
USAGE:
pinata files update [command options] [ID of file]
OPTIONS:
--name value, -n value Update the name of a file
--help, -h show help
```
#### `delete`
```
NAME:
pinata files delete - Delete a file by ID
USAGE:
pinata files delete [command options] [ID of file]
OPTIONS:
--help, -h show help
```
### `groups`
```
NAME:
pinata groups - Interact with file groups
USAGE:
pinata groups command [command options] [arguments...]
COMMANDS:
create, c Create a new group
list, l List groups on your account
update, u Update a group
delete, d Delete a group by ID
get, g Get group info by ID
add, a Add a file to a group
remove, r Remove a file from a group
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help
```
#### `create`
```
NAME:
pinata groups create - Create a new group
USAGE:
pinata groups create [command options] [name of group]
OPTIONS:
--public, -p Determine if the group should be public (default: false)
--help, -h show help
```
#### `get`
```
NAME:
pinata groups get - Get group info by ID
USAGE:
pinata groups get [command options] [ID of group]
OPTIONS:
--help, -h show help
```
#### `list`
```
NAME:
pinata groups list - List groups on your account
USAGE:
pinata groups list [command options] [arguments...]
OPTIONS:
--public, -p List only public groups (default: false)
--amount value, -a value The number of groups you would like to return (default: "10")
--name value, -n value Filter groups by name
--token value, -t value Paginate through results using the pageToken
--help, -h show help
```
#### `add`
```
NAME:
pinata groups add - Add a file to a group
USAGE:
pinata groups add [command options] [group id] [file id]
OPTIONS:
--help, -h show help
```
#### `remove`
```
NAME:
pinata groups remove - Remove a file from a group
USAGE:
pinata groups remove [command options] [group id] [file id]
OPTIONS:
--help, -h show help
```
### `gateways`
```
NAME:
pinata gateways - Interact with your gateways on Pinata
USAGE:
pinata gateways command [command options] [arguments...]
COMMANDS:
set, s Set your default gateway to be used by the CLI
open, o Open a file in the browser
sign, s Get a signed URL for a file by CID
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help
```
#### `set`
Use the command with no arguments and get a list of your gateways to choose from!
```
NAME:
pinata gateways set - Set your default gateway to be used by the CLI
USAGE:
pinata gateways set [command options] [domain of the gateway]
OPTIONS:
--help, -h show help
```
#### `sign`
```
NAME:
pinata gateways sign - Get a signed URL for a file by CID
USAGE:
pinata gateways sign [command options] [cid of the file, number of seconds the url is valid for]
example: pinata gateways sign bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4 30
OPTIONS:
--help, -h show help
```
#### `open`
```
NAME:
pinata gateways open - Open a file in the browser
USAGE:
pinata gateways open [command options] [CID of the file]
OPTIONS:
--help, -h show help
```
### `keys`
```
NAME:
pinata keys - Create and manage generated API keys
USAGE:
pinata keys command [command options] [arguments...]
COMMANDS:
create, c Create an API key with admin or scoped permissions
list, l List and filter API key
revoke, r Revoke an API key
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help
```
#### `create`
```
NAME:
pinata keys create - Create an API key with admin or scoped permissions
USAGE:
pinata keys create [command options] [arguments...]
OPTIONS:
--name value, -n value Name of the API key
--admin, -a Set the key as Admin (default: false)
--uses value, -u value Max uses a key can use (default: 0)
--endpoints value, -e value [ --endpoints value, -e value ] Optional array of endpoints the key is allowed to use
--help, -h show help
```
#### `list`
```
NAME:
pinata keys list - List and filter API key
USAGE:
pinata keys list [command options] [arguments...]
OPTIONS:
--name value, -n value Name of the API key
--revoked, -r Set the key as Admin (default: false)
--exhausted, -e Filter keys that are exhausted or not (default: false)
--uses, -u Filter keys that do or don't have limited uses (default: false)
--offset value, -o value Offset the number of results to paginate
--help, -h show help
```
#### `revoke`
```
NAME:
pinata keys revoke - Revoke an API key
USAGE:
pinata keys revoke [command options] [key]
OPTIONS:
--help, -h show help
```
### `swaps`
```
NAME:
pinata swaps - Interact and manage hot swaps on Pinata
USAGE:
pinata swaps command [command options] [arguments...]
COMMANDS:
list, l List swaps for a given gateway domain or for your config gateway domain
add, a Add a swap for a CID
delete, d Remeove a swap for a CID
help, h Shows a list of commands or help for one command
OPTIONS:
--help, -h show help
```
#### `list`
```
NAME:
pinata swaps list - List swaps for a given gateway domain or for your config gateway domain
USAGE:
pinata swaps list [command options] [cid] [optional gateway domain]
OPTIONS:
--help, -h show help
```
#### `add`
```
NAME:
pinata swaps add - Add a swap for a CID
USAGE:
pinata swaps add [command options] [cid] [swap cid]
OPTIONS:
--help, -h show help
```
#### `delete`
```
NAME:
pinata swaps delete - Remeove a swap for a CID
USAGE:
pinata swaps delete [command options] [cid]
OPTIONS:
--help, -h show help
```
## Contact
If you have any questions please feel free to reach out to us!
[team@pinata.cloud](mailto:team@pinata.cloud)
# Farcaster API
Welcome to the Pinata Farcaster API! The Farcaster API is a free to use API that references the Pinata Hub replicator database, so instead of using the [Hub API](../hub-api-reference/introduction.mdx) you can get much easier to use data at much faster speeds.
To get started you will need a Pinata API Key!
## API Keys
Visit the [Pinata API Keys](https://app.pinata.cloud/developers/api-keys) page to generate new keys.
In the 'New Key' modal, you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, so be sure to give it a name to keep track of it. Once you have that filled out, click "Generate API Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT
again
Once you have created your keys you can go ahead and try testing them! Try to paste this into your terminal with your `JWT`
```bash
curl --request GET \
--url https://api.pinata.cloud/v3/farcaster/users/1 \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see something like this!
```json 200
{
"data": {
"fid": 1,
"custody_address": "0x8773442740c17c9d0f0b87022c722f9a136206ed",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 2,
"follower_count": 7945,
"verifications": ["0x86924c37a93734e8611eb081238928a9d18a63c0"],
"bio": "A sufficiently decentralized social network. farcaster.xyz",
"display_name": "Farcaster",
"pfp_url": "https://i.imgur.com/I2rEbPF.png",
"username": "farcaster"
}
}
```
## Endpoints Overview
### [/casts](/farcaster/api-reference/endpoint/cast-by-hash)
This endpoint is great for fetching a particular cast or by combining with query parameters to get specific lists of casts.
#### Casts by FID
Returns all the casts from user FID `6023`
#### Casts by Following
Returns all the casts from users that FID `6023` is following
#### Casts by Channel
Returns all the casts from a specific channel, e.g. `https://warpcast.com/~/channel/pinata`
### [/channels](/farcaster/api-reference/endpoint/channel-by-name)
The `/channels` endpoint can be used to fetch info about a specific channel or fetch a list of channels
### [/users](/farcaster/api-reference/endpoint/user-by-fid)
This endpoint is similar to `/casts` in that you can either fetch a specific user by FID or you can combine multiple query parameters to get specific lists of users.
#### Users Following FID
Returns all the users following a specific FID
#### Users FID Following
Returns all the users a specific FID is following
# Getting Started
Learn the basics of creating signers for your Farcaster App
To get started with Farcaster Auth you will need to get a [Pinata API Key](/account-management/api-keys) on a paid Pinata account, which you can view pricing [here](https://pinata.cloud/pricing).
You will also need an App FID. Generally you would make a fresh Farcaster account for your app, like [@photocast](https://warpcast.com/photocast) which as an FID of `327481`.
If you are using non-sponsored signers you will also need the mnemoic seed phrase for your app account. This is usually provided to you when creating the account for backup purposes. If you had an env file it might look something kike this:
```
FARCASTER_DEVELOPER_MNEMONIC=pinata confetti ipfs farcaster cloud developer send it
```
### Signer Flow
As stated previously you will need to make sure you have your App FID and App mnemomic phrase somewhere secure where you can access them in the following steps. Doing in Javascript might look something like this.
```plaintext .env
FARCASTER_DEVELOPER_FID=12356
FARCASTER_DEVELOPER_MNEMONIC=pinata confetti ipfs farcaster cloud developer send
```
```typescript index.ts
const appFid = process.env.FARCASTER_DEVELOPER_FID;
const account = mnemonicToAccount(
process.env.FARCASTER_DEVELOPER_MNEMONIC
);
```
After declaring some types and importing `viem/accounts` we can declare the constants and start the process of creating a signer.
```typescript index.ts
import { mnemonicToAccount } from "viem/accounts";
const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = {
name: "Farcaster SignedKeyRequestValidator",
version: "1",
chainId: 10,
verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553",
} as const;
const SIGNED_KEY_REQUEST_TYPE = [
{ name: "requestFid", type: "uint256" },
{ name: "key", type: "bytes" },
{ name: "deadline", type: "uint256" },
] as const;
const appFid = process.env.FARCASTER_DEVELOPER_FID;
const account = mnemonicToAccount(
process.env.FARCASTER_DEVELOPER_MNEMONIC
);
async function createSigner() {
const res = await fetch("https://api.pinata.cloud/v3/farcaster/signers", {
method: "POST",
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
},
body: JSON.stringify({
app_fid: parseInt(appFid, 10)
})
});
const signerInfo: any = await res.json();
console.log(signerInfo)
const { data }: { data: { signer_uuid: string, public_key: string, signer_approved: string } } = signerInfo;
}
```
Now that the key is created we will need to sign the key with our developer Farcaster account
```typescript index.ts
/// previous code
const deadline = Math.floor(Date.now() / 1000) + 86400; // signature is valid for 1 day
const requestFid = parseInt(appFid);
const signature = await account.signTypedData({
domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
types: {
SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE,
},
primaryType: "SignedKeyRequest",
message: {
requestFid: BigInt(appFid),
key: `0x${data.public_key}`,
deadline: BigInt(deadline),
}
});
```
Once the key is signed we can now make a request to Warpcast to register the key. This is where we will get a Farcaster deep link (`farcaster://`) that we can have the user either click on or scan with a QR code that will open the Warpcast app for them to approve the signer.
At this point you can make the decision to use sponsored or non-sponsored signers.
With sponsored signers the end user will not have the pay warps when they approve the sign in, however you may be limited to the number of sponsored signers you may have based on your Pinata plan. Sponsored signers is enabled with the query parameter `?sponsored=true`.
With non-sponsored signers the end user will have to pay warps to approve the signer, however there is no limit to how many non-sponsored signers you can have on your Pinata account.
```typescript index.ts
// previous code
const registerResponse = await fetch(`https://api.pinata.cloud/v3/farcaster/register_signer_with_warpcast?sponsored=true`, {
method: "POST",
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
},
body: JSON.stringify({
signer_id: data.signer_uuid,
signature: signature,
deadline: deadline,
app_fid: requestFid,
app_address: account.address
})
})
const warpcastPayload = await registerResponse.json()
```
At this point we should immediately start polling the signer, waiting for the user to approve the key. Once they do we'll get a response back from Warpcast that will register the key with that user's account and make it accessible via Farcaster Auth. To do that we'll use `poll_warpcast_signer` with a query parameter of `token` that's provided by the previous `warpcastPayload`.
```typescript index.ts
// Previous code...
const pollSigner = await fetch(`https://api.pinata.cloud/v3/farcaster/poll_warpcast_signer?token=${warpcastPayload.data.token}`, {
method: 'POST',
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
}
})
const pollSignerRes = await pollSigner.json()
return pollSignerRes.data.result
```
If successful you should get a response with a `completed` state, at which point you can now use the `signer_id` returned in the Register Signer step.
After registering you can get the `signer_id` at any point using `GET /signers` endpoint with a query parameter of `fid` if you need to filter the results.
```typescript index.ts
async function getSigners(fid?: number){
try {
const req = await fetch(`https://api.pinata.cloud/v3/farcaster/signers?fid=${fid}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer YOUR_PINATA_JWT`,
}
});
const res = await req.json();
const resultData = res.data
return resultData
} catch (error) {
throw error;
}
};
```
With the `signer_id` you can then use it in other API calls like [`POST /casts`](/farcaster/farcaster-api/send-cast) to send a cast.
```bash
curl --location 'https://api.pinata.cloud/v3/farcaster/casts' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_PINATA_JWT' \
--data '{
"castAddBody": {
"text": "Hello World!",
"parentUrl": "https://warpcast.com/~/channel/pinata"
},
"signerId": "SIGNER_ID"
}'
```
# Add Reaction to Cast
post /casts/{hash}/reactions/{type}
# Cast by Hash
get /casts/{hash}
# Casts
get /casts
This endpoint is great for fetching a particular cast or by combining with query parameters to get specific lists of casts.
#### Casts by FID
```
/casts?fid=6023
```
Returns all the casts from user FID `6023`
#### Casts by Following
```
/casts?fid=6023&following=true
```
Returns all the casts from users that FID `6023` is following
#### Casts by Channel
```
/casts?channel=pinata
```
Returns all the casts from a specific channel, e.g. `pinata`
#### Casts by Parent Hash
Returns all the casts fror a specified parent hash
```
/casts?parentHash=0x0ab851ba8524eedf9e164b55f6eeec751f74b539
```
The timestamps returned by the Hub are working off an epoch of `1609459200` or `January 1, 2021 UTC`. Keep this in mind when parsing the time of posted casts.
# Channel By Name
get /channels/{name}
# Channels List
get /channels
# Delete Cast
delete /casts/{hash}
# Delete Reaction to Cast
delete /casts/{hash}/reactions/{type}
# Follow FID
post /follow/{fid}
# Get Signers
get /signers
# Poll Warpcast Signer
post /poll_warpcast_signer
# Register Signer with Warpcast
post /register_signer_with_warpcast
# Send Cast
post /casts
The body of sending a cast is made up of primarily two pieces. One is the `signerId` provided by using [Farcaster Auth](/farcaster/farcaster-auth). The other is the `castAddBody` object which follows the same pattern of Farcaster Hubs when submitting a cast.
```typescript
body = JSON.stringify({
castAddBody: {
text: "Hello World!", // Just a plain text cast
parent_url: "https://warpcast.com/~/channel/pinata", // What channel you want to cast in
embeds: [ // An array of embeds, including links or quote casting
{ // URLs or links are just an object with the url
url: "https://dweb.mypinata.cloud/ipfs/QmYSzs7WczucVrPN2CZcZeEzyGRDrxrFEWGtYA32jz2L3U?filename=sendit.gif"
},
{// Quote casting includes a castId object with the hash of the cast and the FID of the user who sent it
castId: {
fid: 6023,
hash: "0xb3668292af912397fad6b8457223e75c04221992"
}
}
],
mentions: [6023], // An array of FIDs to mention someone in a post
mention_positions: [2], // An array of positions where the mentions should be placed in order of appearance
parent_cast_id: { // Used when replying to a cast, same casdId struct used in embeds
fid: 6023,
hash: "0xb3668292af912397fad6b8457223e75c04221992"
}
},
signerId: "e5cf5d84-7671-4402-8df0-84b0efdc24cd" // The signerId of the user sending a cast, already approved via Farcaster Auth
})
```
# Signers
post /signers
# Unfollow FID
delete /follow/{fid}
# User By FID
get /users/{fid}
# Users
get /users
This endpoint is similar to `/casts` in that you can either fetch a specific user by FID or you can combine multiple query parameters to get specific lists of users.
#### Users Following FID
```
/users?fid=6023&followers=true
```
Returns all the users following a specific FID
#### Users FID Following
```
/users?fid=6023&following=true
```
Returns all the users a specific FID is following
# Getting Started
Welcome to the Pinata Farcaster API! The Farcaster API is a free to use API that references the Pinata Hub replicator database, so instead of using the [Hub API](../hub-api-reference/introduction.mdx) you can get much easier to use data at much faster speeds.
To get started you will need a Pinata API Key!
## API Keys
Visit the [Pinata API Keys](https://app.pinata.cloud/developers/api-keys) page to generate new keys.
In the 'New Key' modal, you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, so be sure to give it a name to keep track of it. Once you have that filled out, click "Generate API Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT
again
Once you have created your keys you can go ahead and try testing them! Try to paste this into your terminal with your `JWT`
```bash
curl --request GET \
--url https://api.pinata.cloud/v3/farcaster/users/1 \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see something like this!
```json 200
{
"data": {
"fid": 1,
"custody_address": "0x8773442740c17c9d0f0b87022c722f9a136206ed",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 2,
"follower_count": 7945,
"verifications": ["0x86924c37a93734e8611eb081238928a9d18a63c0"],
"bio": "A sufficiently decentralized social network. farcaster.xyz",
"display_name": "Farcaster",
"pfp_url": "https://i.imgur.com/I2rEbPF.png",
"username": "farcaster"
}
}
```
## Endpoints Overview
### [`/casts`](/farcaster/api-reference/endpoint/cast-by-hash)
This endpoint is great for fetching a particular cast or by combining with query parameters to get specific lists of casts.
#### Casts by FID
Returns all the casts from user FID `6023`
#### Casts by Following
Returns all the casts from users that FID `6023` is following
#### Casts by Channel
Returns all the casts from a specific channel, e.g. `https://warpcast.com/~/channel/pinata`
### [`/channels`](/farcaster/api-reference/endpoint/channel-by-name)
The `/channels` endpoint can be used to fetch info about a specific channel or fetch a list of channels
### [`/users`](/farcaster/api-reference/endpoint/user-by-fid)
This endpoint is similar to `/casts` in that you can either fetch a specific user by FID or you can combine multiple query parameters to get specific lists of users.
#### Users Following FID
Returns all the users following a specific FID
#### Users FID Following
Returns all the users a specific FID is following
# Farcaster Auth
The easiest way to manage signers for your Farcaster applications
One of the most difficult things to do in the Farcaster ecosystem is allowing users to make writes to the network from your app. Thankfully Pinata makes this easy with Farcaster Auth.
## Primer to Farcaster Signers
In the Farcaster ecosystem there are a few ways you can make writes to the network, and they all revolve around the ED25519 signer. All Farcaster accounts are sets of public and private keypairs, or simply EOA wallets. Instead of prompting users to paste in their mnemonic seed phrase into an app, the more popular approach is to create a new keypair that the user can approve to be used to write on their behalf. This is much safer as the user can revoke these keypairs at any time.
The only problem is managing the signer key. Some developers store the key in a user's local storage, which could get deleted if they clear their browser history or do some kind of cache clear. Others might manage the signers themselves in their database, but there are liability risks here. Farcaster Auth takes both of those problems away with an API and SDK that can mange the signers and create a simpler flow for users.
## How it works
A high level view of Farcaster Auth looks something like this
* Create a new signer with `POST /signers`
* Sign the public key returned by the API with your `FARCASTER_DEVELOPER_MNEMONIC`
* Send a request with the signature payload to register the signer with warpcast
* API returns a polling `token` and a `deep_link_url`. Use the `token` to check the status of the approval, then give the end user the `deep_link_url` in the form of a QR code or link that will open Warpcast
* Continue to poll the token, and once the signer is approved the process will be complete. The `signer_id` at this point can be used to make post requests.
### Using Signers
Once you have an approved signer you can fetch the `signerId` from `GET /signers?fid=USER_FID` and filter by FID if you need to. Once you have the `signerId` you can use it in other endpoints like sending casts.
## Getting Started
To get started with Farcaster Auth you will need to get a [Pinata API Key](/account-management/api-keys) on a paid Pinata account, which you can view pricing [here](https://pinata.cloud/pricing).
You will also need an App FID. Generally you would make a fresh Farcaster account for your app, like [@photocast](https://warpcast.com/photocast) which as an FID of `327481`.
You will also need the mnemoic seed phrase for your app account, as this will sign the key and let the user know what app they are signing into when approving it in Warpcast. This is usually provided to you when creating the account for backup purposes. If you had an env file it might look something kike this:
```
FARCASTER_DEVELOPER_MNEMONIC=pinata confetti ipfs farcaster cloud developer send it
```
### Signer Flow
As stated previously you will need to make sure you have your App FID and App mnemomic phrase somewhere secure where you can access them in the following steps. Doing in Javascript might look something like this.
```plaintext .env
FARCASTER_DEVELOPER_FID=12356
FARCASTER_DEVELOPER_MNEMONIC=pinata confetti ipfs farcaster cloud developer send
```
```typescript index.ts
const appFid = process.env.FARCASTER_DEVELOPER_FID;
const account = mnemonicToAccount(
process.env.FARCASTER_DEVELOPER_MNEMONIC
);
```
After declaring some types and importing `viem/accounts` we can declare the constants and start the process of creating a signer.
```typescript index.ts
import { mnemonicToAccount } from "viem/accounts";
const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = {
name: "Farcaster SignedKeyRequestValidator",
version: "1",
chainId: 10,
verifyingContract: "0x00000000fc700472606ed4fa22623acf62c60553",
} as const;
const SIGNED_KEY_REQUEST_TYPE = [
{ name: "requestFid", type: "uint256" },
{ name: "key", type: "bytes" },
{ name: "deadline", type: "uint256" },
] as const;
const appFid = process.env.FARCASTER_DEVELOPER_FID;
const account = mnemonicToAccount(
process.env.FARCASTER_DEVELOPER_MNEMONIC
);
async function createSigner() {
const res = await fetch("https://api.pinata.cloud/v3/farcaster/signers", {
method: "POST",
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
},
body: JSON.stringify({
app_fid: parseInt(appFid, 10)
})
});
const signerInfo: any = await res.json();
console.log(signerInfo)
const { data }: { data: { signer_uuid: string, public_key: string, signer_approved: string } } = signerInfo;
}
```
Now that the key is created we will need to sign the key with our developer Farcaster account
```typescript index.ts
/// previous code
const deadline = Math.floor(Date.now() / 1000) + 86400; // signature is valid for 1 day
const requestFid = parseInt(appFid);
const signature = await account.signTypedData({
domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
types: {
SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE,
},
primaryType: "SignedKeyRequest",
message: {
requestFid: BigInt(appFid),
key: `0x${data.public_key}`,
deadline: BigInt(deadline),
}
});
```
Once the key is signed we can now make a request to Warpcast to register the key.
Since this is non-sponsored managed signers the user will need to pay warps at least once. After that the key is linked to your Pinata account and remains active until the usere revokes it.
```typescript index.ts
// previous code
const registerResponse = await fetch(`https://api.pinata.cloud/v3/farcaster/register_signer_with_warpcast`, {
method: "POST",
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
},
body: JSON.stringify({
signer_id: data.signer_uuid,
signature: signature,
deadline: deadline,
app_fid: requestFid,
app_address: account.address
})
})
const warpcastPayload = await registerResponse.json()
//"data": {
// "signer_id": "ee5605a0-9833-4d30-8c22-7ac315061ee7",
// "token": "0x2ef159dc1e9f6a2sb0d2353f",
// "deep_link_url": "farcaster://signed-key-request?token=0x2efb59dv1e9f642fb07f353f",
// "status": "pending_approval"
//}
```
At this point we should immediately start polling the signer, waiting for the user to approve the key. You might have noticed in the response to `/register_signer_with_warpcast` is the `deep_link_url`. This is a special URL that when opened on a mobile device with Warpcast installed, will prompt the user to approve the signer/login. This can be given to the user in the form of a QR code or just a link that the user opens.
Once they approve the signer we'll get a response back from Warpcast that will register the key with that user's account and make it accessible via Farcaster Auth. To do that we'll use `poll_warpcast_signer` with a query parameter of `token` that's provided by the previous request to `register_signer_with_warpcast`.
```typescript index.ts
// Previous code...
const pollSigner = await fetch(`https://api.pinata.cloud/v3/farcaster/poll_warpcast_signer?token=${warpcastPayload.data.token}`, {
method: 'POST',
headers: {
'Content-Type': "application/json",
"Authorization": `Bearer YOUR_PINATA_JWT`
}
})
const pollSignerRes = await pollSigner.json()
return pollSignerRes.data.result
//"signedKeyRequest": {
// "token": "0x2ef159dc1e9f6a2sb0d2353f",
// "deeplinkUrl": "farcaster://signed-key-request?token=0x2efb59dv1e9f642fb07f353f",
// "key": "0xe57c09c48b0ba27d95676309416134ad1409d3cg27ccb354b73eb819aa117f2f",
// "requestFid": 6023,
// "state": "completed",
// "isSponsored": false,
// "userFid": 6023
//}
```
If successful you should get a response with a `completed` state, at which point you can now use the `signer_id` returned in the Register Signer step.
After registering you can get the `signer_id` at any point using `GET /signers` endpoint with a query parameter of `fid` if you need to filter the results.
```typescript index.ts
async function getSigners(fid?: number){
try {
const req = await fetch(`https://api.pinata.cloud/v3/farcaster/signers?fid=${fid}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer YOUR_PINATA_JWT`,
}
});
const res = await req.json();
const resultData = res.data
return resultData
} catch (error) {
throw error;
}
};
//"signers": [
// {
// "id": 25,
// "signer_uuid": "bd9cea0c-ab31-4c71-a46f-a169ba626192",
// "fid": 6023,
// "public_key": "e57c09c48b0ba27d95676309416134ad1409d3cg27ccb354b73eb819aa117f2f",
// "signer_approved": true,
// "revoked": false
// }
//],
//"next_page_token": "eyJvZmZzZXQiOiIyNSJ9"
```
With the `signer_id` you can then use it in other API calls like [`POST /casts`](/farcaster/farcaster-api/send-cast) to send a cast.
```bash
curl --location 'https://api.pinata.cloud/v3/farcaster/casts' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_PINATA_JWT' \
--data '{
"castAddBody": {
"text": "Hello World!",
"parentUrl": "https://warpcast.com/~/channel/pinata"
},
"signerId": "SIGNER_ID"
}'
```
# Farcaster Dev Calls
A directory of dev call notes from the Pinata team
[March 28th, 2024](/farcaster/fc-dev-calls/032824)
[April 11th, 2024](/farcaster/fc-dev-calls/041124)
[April 25th, 2024](/farcaster/fc-dev-calls/042524)
[May 23rd, 2024](/farcaster/fc-dev-calls/052324)
[June 6th, 2024](/farcaster/fc-dev-calls/060624)
[June 20th, 2024](/farcaster/fc-dev-calls/062024)
[July 2nd, 2024](/farcaster/fc-dev-calls/070224)
[July 18th, 2024](/farcaster/fc-dev-calls/071824)
[August 1st, 2024](/farcaster/fc-dev-calls/080124)
# Farcaster Auth
Using the FDK with Farcaster Auth
The FDK makes it easy to give your Farcaster app write access for users so you can do things like sending casts or following other users. To get a better concept of the flow of Farcaster Auth, check out the guide [here](/farcaster/farcaster-auth).
## `createSigner`
This function will create a signer with Farcaster Auth, sign the key with your Farcaster App mnemonic phrase and FID, then send a request to Warpcast to register the signer. For more info on those please see [these docs](https://docs.pinata.cloud/farcaster/farcaster-auth#getting-started).
In order to use it make sure the mnemonic and FID are included with the PinataFDK instance.
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: 'YOUR_PINATA_JWT',
pinata_gateway: 'YOUR_GATEWAY',
app_fid: 'APP_FID',
app_mnemonic: 'APP_MNEMONIC'
})
```
### Example
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
app_fid: `${process.env.APP_FID}`
app_mnemonic: `${process.env.FARCASTER_DEVELOPER_MNEMONIC}`
})
const signerData: WarpcastPayload = await fdk.createSigner()
```
After creating the signer the user would visit the `signerData.deep_link_url` which would open Warpcast on their account to approve the signer. The user will have to pay warps since it is not sponsored.
### Response
```typescript
{
signer_id: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
token: "0xe3bffad26b16cf825f3d062d",
deep_link_url: "farcaster://signed-key-request?token=0xe3bffad26b16cf825f3d062d",
status: "pending_approval"
}
```
## `createSponsoredSigner`
Sponsored signers is very similar to `createSigner` except the end user will not have to pay warps to approve the signer. However there may be limits to how many sponsored signers you can use based on your Pinata plan. You still need the `app_mnemonic` to sign the key being used.
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: 'YOUR_PINATA_JWT',
pinata_gateway: 'YOUR_GATEWAY',
app_fid: 'APP_FID',
app_mnemonic: 'APP_MNEMONIC'
})
```
### Example
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
app_fid: `${process.env.APP_FID}`,
app_mnemonic: `${process.env.FARCASTER_DEVELOPER_MNEMONIC}`
})
const signerData: WarpcastPayload = await fdk.createSponsoredSigner()
```
After creating the signer the user would visit the `signerData.deep_link_url` which would open Warpcast on their account to approve the signer. Since it is sponsored the user will not have to pay warps to sign in, however it will show Pinata as the app.
### Response
```typescript
{
signer_id: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
token: "0x21658c8fa560aca0f35a5e4a",
deep_link_url: "farcaster://signed-key-request?token=0x21658c8fa560aca0f35a5e4a",
status: "pending_approval"
}
```
## `pollSigner`
After creating a signer and giving the user the `deep_link_url` you will want to poll the signer to see if they have approved it and record the response to your account.
### Params
`pollSigner` takes a parameter of `token` which is provided in either `createSigner` or `createSponsoredSigner`.
### Example
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
app_fid: `${process.env.APP_FID}`
})
const pollData = await fdk.pollSigner("0x21658c8fa560aca0f35a5e4a")
```
### Returns
```typescript
{
token: "0x321bbb927d9009232a7c26d6",
deeplinkUrl: "farcaster://signed-key-request?token=0x321bbb927d9009232a7c26d6",
key: "0x858e9ed1af97ec0c1cf06e7d769a2bca9ec324c152f320ee34b253af27b486f4",
requestFid: 20918,
state: "pending",
isSponsored: false
}
```
## `getSigner`
After you have created a signer and it has been polled as complete, you can fetch the signer at any time using `getSigner`
### Params
* fid - A number/integer of the FID you want to query. (Optional)
### Example
```typescript
import { PinataFDK } from "pinata-fdk"
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
app_fid: `${process.env.APP_FID}`
})
const pollData = await fdk.getSigner(6023)
```
### Returns
```typescript
{
signers: [
{
id: 57,
signer_uuid: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
fid: 6023,
public_key: "dad973170c63739f7c812d188fab1df074eb1cd48facf6556e2ef9cbb76b4c18",
signer_approved: true,
revoked: false
}
],
next_page_token: "eyJvZmZzZXQiOiI1NyJ9"
}
```
# Farcaster Frames
Using the FDK to build frames
The FDK can be used to not only create frames but also provide analytics through your Pinata account.
## `getFrameMetadata`
You can use this function to easily create the Farcaster specific metadata needed for your frame.
**The only required input is `cid` or `image`.**
### Params
* `buttons` - An array of button specifications (max 4). (Optional)
* `image` - A string for a valid hosted image url.
* `cid` - A string representing the cid of an IPFS pinned image.
* `input` - A string representing the text displayed for text input. (Optional)
* `post_url` - A string which contains a valid URL to send the Signature Packet to. (Optional)
* `refresh_period` - A string representing the refresh period for the image used. (Optional)
* `aspect_ratio` - A string representing the aspect ratio for the image used. (Optional)
* `state` - An object (e.g. JSON) representing the state data for the frame. (Optional)
**Note:** state should only be included in response frames, not initial frames.
```javascript
type FrameHTMLType = {
buttons?: [FrameButtonMetadata, ...FrameButtonMetadata[]];
image?: {url: string};
cid?: string;
input?: FrameInputMetadata;
post_url?: string;
refresh_period?: number;
aspect_ratio?: "1.91:1" | "1:1";
state?: object;
} & (
{ image: {url: string}}| { cid: string }
);
```
```javascript
type FrameButtonMetadata = {
label: string;
action?: "post" | "post_redirect" | "mint" | "link" | "tx";
target?: string;
}
```
```javascript
type FrameInputMetadata = {
text: string;
};
```
### Example Code
```javascript
const frameMetadata = fdk.getFrameMetadata({
post_url: `/api/test`,
input: {text: "Hello, world!"},
aspectRatio: "1.91:1",
buttons: [
{ label: 'Click me', action: 'post'},
{ label: 'Button 2', action: "post_redirect"},
{ label: 'Button 3', action: "mint" },
{ label: 'Button 4', action: "link" },
],
cid: "",
state: {counter: 1}
});
```
### Response
```javascript
```
### Images for `getFrameMetadata`
There are two different ways to set the images of your frame metadata.
⚡️ Raw URL
⚡️ CID
### Raw URL
Specify a hosted url image link.
```javascript
const frameMetadata = fdk.getFrameMetadata({
image: { url: ""}
});
```
#### CID
Specify a CID from your Pinata account.
```javascript
const frameMetadata = fdk.getFrameMetadata({
cid: "QmX63EYiDk9cExrv4GDmZ5soJKkgqoUJv9LbtPyugLBtV2"
});
//Must insert Pinata credentials when intializing SDK.
```
## `convertUrlToIPFS`
Uploads an image to IPFS from a url. This url may be passed to the `getFrameMetadata` function.
#### Params
* `url` - A string for a valid hosted url image.
### Example Code
```javascript
const ipfsUrl = await fdk.convertUrlToIPFS("https://example.com");
const frameMetadata = fdk.getFrameMetadata({
image: { url: ipfsUrl}
});
```
### Response
```javascript
https:///ipfs/
```
## `validateFrameMessage`
Returns a Promise that indicates wether a message signature is valid by querying Pinata's Farcaster hub.
### Params
* `body` - An object representing the raw payload of an action frame produced by Farcaster.
```
{
untrustedData: {
fid: 2,
url: "https://fcpolls.com/polls/1",
messageHash: "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
timestamp: 1706243218,
network: 1,
buttonIndex: 2,
inputText: "hello world", // "" if requested and no input, undefined if input not requested
castId: {
fid: 226,
hash: "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9",
},
},
trustedData: {
messageBytes: "d2b1ddc6c88e865a33cb1a565e0058d757042974...",
},
};
```
### Example Code
```javascript
const { isValid, message } = await fdk.validateFrameMessage(body);
```
### Response
```javascript
{
isValid: true,
message: {
data: {
type: 13,
fid: 15974,
timestamp: 98469569,
network: 1,
castAddBody: undefined,
castRemoveBody: undefined,
reactionBody: undefined,
verificationAddAddressBody: undefined,
verificationRemoveBody: undefined,
userDataBody: undefined,
linkBody: undefined,
usernameProofBody: undefined,
frameActionBody: [Object]
},
hash: ,
hashScheme: 1,
signature: ,
signatureScheme: 1,
signer: ,
dataBytes: undefined
}
}
```
## `getAddressForFid`
Returns the connected Ethereum address for an FID.
### Params
* `fid` - A number representing the fid of the user.
### Example Code
```javascript
const address = await fdk.getAddressForFid(15974);
```
### Response
```
"0x9b7c18a71a98acd2f1271e2d1fe63750a70bc52b"
```
# Frame Analytics
To get started visit the [Integrations page](https://app.pinata.cloud/integrations) by clicking on the profile button in the top right, then selecting Integrations.
Once there you can click on "Sign In" which will bring up a QR code you can scan with you phone.
You will need [Warpcast](https://warpcast.com) installed on your phone to sign in
Scanning the QR code with your phone should prompt you to open Warpcast where you can sign in and approve the integration.
In any of your POST endpoints for frames, you can send analytics like this:
## `sendAnalytics`
Sends data to Pinata analytics for a specific frame.
### Params
* `frame_data` - An object representing the raw payload of an action frame produced by Farcaster.
* `frame_id` - A string representing the frame you want to track.
* `custom_id`: A string representing a unique identifier to segment requests within the specified frame. (Optional)
### Example Code
```javascript
// This should be the raw payload from the frame action
const frame_data = {
untrustedData: {
fid: 2,
url: "https://fcpolls.com/polls/1",
messageHash: "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
timestamp: 1706243218,
network: 1,
buttonIndex: 2,
inputText: "hello world", // "" if requested and no input, undefined if input not requested
castId: {
fid: 226,
hash: "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9",
},
},
trustedData: {
messageBytes: "d2b1ddc6c88e865a33cb1a565e0058d757042974...",
},
};
const frame_id = "my-unique-frame-name"
const custom_id = "my_custom_id"
await fdk.sendAnalytics(frame_id, frame_data, custom_id)
```
#### Response
```
{success: true}
```
After this is deployed you will see the analytics on the [Frame Analytics Page](https://app.pinata.cloud/frames-analytics).
### Frog Analytics Plug-in 🐸
If you are using the Frog framework, you can utilize Pinata Frame analytics by importing our custom middleware function `analyticsMiddleware`.
```typescript
import { PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: "",
pinata_gateway: ""},
);
const app = new Frog({
basePath: '/api',
// hubApiUrl: "https://hub.pinata.cloud"
})
app.use("/", fdk.analyticsMiddleware({ frameId: "frame_id", customId: "custom_id"}));
```
# Pin Files from CLI
## npx pin
Pin files directly from your `src` folder using the **pinata-fdk** `npx pin` command.
Create a `pins` folder located under your `src` folder
```jsx
src
-pins/
```
Add any images you want uploaded to IPFS!
```jsx
src
-pins/
--image.png
```
Run the command:
```jsx
npx pin
```
Check your `src/pins` folder for a `pins_list.json` file. Here you can easily access your CID to input into `getFrameMetadata`.
# Farcaster Reads
Use the FDK to make reads from the Farcaster Network
These methods can be used to view casts, channels and users. All of them require a PinataFDK initialization with a Pinata JWT.
## `getCasts`
With the `getCasts` method you can view a paginated list of casts on the Farcaster Network.
### Params
* pageToken - A pagination token to view the next page of casts. (Optional)
### Example
```typescript
import { Casts, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const pageToken = "eyJvZmZzZXQiOiIyODg3ODMzNTExNzY5NjcwOTk5In0"
const res: Casts = await fdk.getCasts(pageToken);
```
### Response
```typescript
{
"casts": [
{
"fid": 192,
"hash": "0x27ec7197ace4365b6004ae4f729c7b46cbc569e2",
"short_hash": "0x27ec7197",
"thread_hash": null,
"parent_hash": null,
"parent_url": null,
"root_parent_url": null,
"parent_author": null,
"author": {
"uid": 192,
"fid": 192,
"custody_address": "0xc2e7484fbd2322a9986bc49f2e87d7fcac019e26",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 4,
"follower_count": 52,
"verifications": [],
"bio": "@bunches | product, design, quality",
"display_name": "Tomer Ben-David",
"pfp_url": "https://warpcast.com/avatar.png?t=1709148185820",
"username": "tome",
"power_badge_user": false
},
"content": "gm everybody, gm",
"timestamp": "2021-11-12T19:22:34Z",
"embeds": [],
"mentions": [],
"mentionPositions": [],
"reactions": {
"likes": [
{
"fid": 192,
"fname": "tome"
}
]
},
"replies": {
"count": 0
},
"mentioned_profiles": []
},
...
],
"next_page_token": "eyJvZmZzZXQiOiIyODg3ODMzNTExNzY5NjcwOTk5In0"
}
```
## `getCastByHash`
With the `getCastByHash` method you can view a specific cast on the Farcaster Network.
### Params
* hash - The specific hash of the desired cast.
### Example
```typescript
import { Cast, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const hash = "0x27ec7197ace4365b6004ae4f729c7b46cbc569e2"
const res: Cast = await fdk.getCastByHash(hash);
```
### Response
```typescript
{
"fid": 192,
"hash": "0x27ec7197ace4365b6004ae4f729c7b46cbc569e2",
"short_hash": "0x27ec7197",
"thread_hash": null,
"parent_hash": null,
"parent_url": null,
"root_parent_url": null,
"parent_author": null,
"author": {
"uid": 192,
"fid": 192,
"custody_address": "0xc2e7484fbd2322a9986bc49f2e87d7fcac019e26",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 4,
"follower_count": 52,
"verifications": [],
"bio": "@bunches | product, design, quality",
"display_name": "Tomer Ben-David",
"pfp_url": "https://warpcast.com/avatar.png?t=1709148185820",
"username": "tome",
"power_badge_user": false
},
"content": "gm everybody, gm",
"timestamp": "2021-11-12T19:22:34Z",
"embeds": [],
"mentions": [],
"mentionPositions": [],
"reactions": {
"likes": [
{
"fid": 192,
"fname": "tome"
}
]
},
"replies": {
"count": 0
},
"mentioned_profiles": []
}
```
## `getUsers`
With the `getUsers` method you can view a paginated list of users on the Farcaster Network.
### Params
* pageToken - A pagination token to view the next page of users. (Optional)
### Example
```typescript
import { Users, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const pageToken = "eyJvZmZzZXQiOiIxMDAifQ"
const res: Users = await fdk.getUsers(pageToken);
```
### Response
```typescript
{
"users": [
{
"fid": 1,
"custody_address": "0x8773442740c17c9d0f0b87022c722f9a136206ed",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 2,
"follower_count": 12991,
"verifications": [
"0x86924c37a93734e8611eb081238928a9d18a63c0"
],
"bio": "A sufficiently decentralized social network. farcaster.xyz",
"display_name": "Farcaster",
"pfp_url": "https://i.imgur.com/I2rEbPF.png",
"username": "farcaster",
"power_badge_user": true
},
...
],
"next_page_token": "eyJvZmZzZXQiOiIxMDAifQ"
}
```
## `getUserByFid`
With the `getUserByFid` method you can view a specific user on the Farcaster Network.
### Params
* fid - The specific fid of the desired user.
### Example
```typescript
import { User, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const fid = 20591
const res: User = await fdk.getUserByFid(fid);
```
### Response
```typescript
{
"fid": 20591,
"custody_address": "0x62402434701e0ce0ae4ea4b3caf68230a6ddbe43",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 13,
"follower_count": 351,
"verifications": [
"0x5ba9355a10611ed4339526472a59befed3549bad"
],
"bio": "Everyone is from somewhere. Cofounder and CEO of Pinata. https://www.pinata.cloud/farcaster",
"display_name": "Kyle Tut",
"pfp_url": "https://i.imgur.com/TLMFnH6.jpg",
"username": "kyletut",
"power_badge_user": true
}
```
## `getChannelsFollowing`
With the `getChannelsFollowing` method you can view a paginated list of the channels a specific user is following on the Farcaster Network.
### Params
* fid - The specific fid of the desired user.
* pageToken - A pagination token to view the next page of channels followed. (Optional)
### Example
```typescript
import { ChannelsFollowing, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const fid = 3
const pageToken = "eyJwYWdlIjoxLCJsaW1pdCI6MjV9"
const res: ChannelsFollowing = await fdk.getChannelsFollowing(fid, pageToken);
```
### Response
```typescript
{
"channels": [
{
"id": "aoe2",
"name": "aoe2",
"url": "https://warpcast.com/~/channel/aoe2",
"description": "For fans of Age of Empires 2",
"image_url": "https://i.imgur.com/q7dolW5.png",
"lead_fid": 3,
"created_at": 1709398612,
"host_fids": [
3
],
"follower_count": 58,
"followed_at": 1709398613
},
...
],
"next_page_token": "eyJwYWdlIjoxLCJsaW1pdCI6MjV9"
}
```
## `getChannelFollowingStatus`
With the `getChannelFollowingStatus` method you can check if a specific user follows a specific channel on the Farcaster Network.
### Params
* fid - The specific fid of the desired user.
* name - The specific name of the desired channel.
### Example
```typescript
import { ChannelFollowingStatus, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const fid = 20591
const name = "pinata"
const res: ChannelFollowingStatus = await fdk.getChannelFollowingStatus(fid, pageToken);
```
### Response
```typescript
{
"following": true,
"followed_at": 1706890562
}
```
## `getChannels`
With the `getChannels` method you can view a paginated list of channels on the Farcaster Network.
### Params
* pageToken - A pagination token to view the next page of channels. (Optional)
### Example
```typescript
import { Channels, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const pageToken = "eyJvZmZzZXQiOiIxMDAifQ"
const res: Channels = await fdk.getChannels(pageToken);
```
### Response
```typescript
{
"channels": [
{
"name": "pinata",
"url": "https://warpcast.com/~/channel/pinata",
"display_name": "pinata",
"description": "Build on IPFS and Farcaster at Scale | https://docs.pinata.cloud/farcaster/farcaster-api/getting-started",
"image_url": "https://i.imgur.com/u4mGk5b.gif",
"lead_fid": 20591,
"created_at": 1706890562,
"host_fids": [
20591,
4823,
6023
],
"follower_count": 494
},
...
],
"next_page_token": "eyJvZmZzZXQiOiIxMDAifQ"
}
```
## `getChannelByName`
With the `getChannelByName` method you view a specific channel queried by name on the Farcaster Network.
### Params
* name - The specific name of the desired channel.
### Example
```typescript
import { Channel, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const name = "pinata"
const res: Channel = await fdk.getChannelByName(name);
```
### Response
```typescript
{
"name": "pinata",
"url": "https://warpcast.com/~/channel/pinata",
"display_name": "pinata",
"description": "Build on IPFS and Farcaster at Scale | https://docs.pinata.cloud/farcaster/farcaster-api/getting-started",
"image_url": "https://i.imgur.com/u4mGk5b.gif",
"lead_fid": 20591,
"created_at": 1706890562,
"host_fids": [
20591,
4823,
6023
],
"follower_count": 494
}
```
## `getChannelFollowers`
With the `getChannelFollowers` method you can view a paginated list of a specific channel's followers on the Farcaster Network.
### Params
* name - The specific name of the desired channel.
* pageToken - A pagination token to view the next page of channel followers. (Optional)
### Example
```typescript
import { ChannelFollowers, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const name = "pinata"
const pageToken = "eyJwYWdlIjoxLCJsaW1pdCI6MTAwfQ"
const res: ChannelFollowers = await fdk.getChannelFollowers(name, pageToken);
```
### Response
```typescript
{
"followers": [
{
"fid": 269317,
"followed_at": 1710359017
},
{
"fid": 325467,
"followed_at": 1710357737
}
...
],
"next_page_token": "eyJwYWdlIjoyLCJsaW1pdCI6MTAwfQ"
}
```
# Farcaster Writes
Use the FDK to make writes to the Farcaster Network
These methods can be used to create casts, react to them, or even follow users. All of them require a `signerId` which is a result of using [Farcaster Auth](/farcaster/farcaster-auth), as well as the PinataFDK initialization with a Pinata JWT.
All of these methods also return the same response, which is the direct response from the Pinata Hub.
```typescript
type CastResponse = {
data: {
type: string;
fid: number;
timestamp: number;
network: string;
castAddBody: {
embedsDeprecated: any[];
mentions: any[];
text: string;
mentionsPositions: any[];
embeds: any[];
};
};
hash: string;
hashScheme: string;
signature: string;
signatureScheme: string;
signer: string;
dataBytes: string;
};
```
## `sendCast`
With the `sendCast` method you can effortlessly post to Farcaster using a `signerId`.
### Params
* CastRequest - An object that contains the `signerId` and the `castAddBody` which follows the [Farcaster Hub standard for sending casts](https://docs.farcaster.xyz/reference/hubble/datatypes/messages#_3-1-castaddbody).
```typescript
type CastRequest = {
signerId: string;
castAddBody: CastBody;
}
type CastBody = {
embedsDeprecated?: string[];
mentions?: number[];
parentCastId?: CastId | null;
parentUrl?: string | null;
text?: string | null;
mentionsPositions?: number[] | null;
embeds?: Embed[] | null;
}
type CastId = {
fid: number;
hash: string;
}
type Embed = {
url?: string | null;
castId?: CastId | null;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const res: CastResponse = await fdk.sendCast({
castAddBody: {
text: "Hello World from !",
mentions: [6023],
mentionsPositions: [18],
parentUrl: "https://warpcast.com/~/channel/pinata",
embeds: [
{
url: "https://pinata.cloud"
},
{
castId: {
fid: 6023
hash: "0xcae8abd9badbb60c9b610ec264f42ed9f1785c6f",
}
}
],
parentCastId: {
fid: 6023,
hash: "0xcae8abd9badbb60c9b610ec264f42ed9f1785c6f"
}
},
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1"
});
```
## `deleteCast`
This method can delete a cast with a provided target hash.
### Params
* hash - Target hash of the cast that needs to be deleted
* signerId - Signer for the cast
```typescript
type CastDelete = {
hash: string;
signerId: string;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const deleteReq: CastResponse = await fdk.deleteCast({
hash: "0x490889854a4f3233433b1ad0560f016f04feeeff",
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `likeCast`
This method can like a cast based on the provided target hash.
### Params
* hash - Hash of the target cast to be liked
* signerId - Signer of the user liking the cast
```typescript
type LikeCast = {
hash: string,
signerId: string
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const likeReq: CastResponse = await fdk.likeCast({
hash: "0x490889854a4f3233433b1ad0560f016f04feeeff",
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `unlikeCast`
If a cast is already liked by the user, this method will unlike it.
### Params
* hash - Hash of the target cast to be unliked
* signerId - Signer of the user unliking the cast
```typescript
type LikeCast = {
hash: string,
signerId: string
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const unlikeReq: CastResponse = await fdk.unlikeCast({
hash: "0x490889854a4f3233433b1ad0560f016f04feeeff",
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `recastCast`
This method will recast a cast based on the target hash.
### Params
* hash - Hash of the target cast to be recast
* signerId - Signer of the user recasting target cast
```typescript
type RecastCast = {
hash: string;
signerId: string;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const recastCastReq: CastResponse = await fdk.recastCast({
hash: "0x490889854a4f3233433b1ad0560f016f04feeeff",
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `removeRecast`
Works just like `recastCast` but removes an existing recast based on a hash of the target cast.
### Params
* hash - Hash of the target cast to remove recast
* signerId - Signer of the user removing the recast
```typescript
type RecastCast = {
hash: string;
signerId: string;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const removeRecastReq: CastResponse = await fdk.removeRecast({
hash: "0x490889854a4f3233433b1ad0560f016f04feeeff",
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `followUser`
Follows a target user based on their FID.
### Params
* fid - The FID of the target user to follow
* signerId - The signer for the user following the target user
```typescript
type FollowUser = {
fid: number;
signerId: string;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const followUserReq: CastResponse = await fdk.followUser({
fid: 6023,
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
## `unfollowUser`
Unfollows a user that the signer is already following.
### Params
* fid - The FID of the target user to unfollow
* signerId - The signer for the user unfollowing the target user
```typescript
type FollowUser = {
fid: number;
signerId: string;
}
```
### Example
```typescript
import { CastResponse, PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: `${process.env.PINATA_JWT}`,
pinata_gateway: "",
});
const unfollowUserReq: CastResponse = await fdk.unfollowUser({
fid: 6023,
signerId: "ba2d9f6d-7514-4967-8b52-5a040b7da4a1",
};
```
# Getting Started
Learn how to the get up and running with the Farcaster Development Kit
The Farcaster Development Kit (FDK) is an SDK to make it easier to manage [Farcaster Auth](/farcaster/farcaster-auth), [Farcaster Writes](/farcaster/fdk/farcaster-writes), [Farcaster Reads](/farcaster/fdk/farcaster-reads), and [Farcaster Frames](/farcaster/fdk/farcaster/frames).
## Installation
```bash
npm i pinata-fdk
```
```bash
yarn add pinata-fdk
```
## Initialization
If you are going to be using Farcaster Auth you will need to provide your Farcaster App FID, and if you are using non-sponsored signers you will need to provide your mnemonic phrase as well.
```javascript
import { PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: "",
pinata_gateway: "", // gateway can be blank in this instance
appFid: "",
appMnemonic: ""
});
```
If you are using just Farcaster Writes or Frame Analytics you only need the Pinata JWT in the instance.
```javascript
import { PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: "",
pinata_gateway: "", // gateway can be blank in this instance
});
```
If you want to leverage IPFS pinning capabilities, you must enter your Pinata JWT and a Pinata gateway during intialization.
```javascript
import { PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK({
pinata_jwt: "",
pinata_gateway: ""},
);
```
If you are only using the frame metadata functionality, you do not need to enter your credentials.
```javascript
import { PinataFDK } from "pinata-fdk";
const fdk = new PinataFDK();
```
# Get Interactions
get /interactions
# Get Top Interactions
get /interactions/top
# Getting Started
Welcome to the Frame Analytics API! This API will give you finer grain access to Frame Analytics than the [FDK](/farcaster/fdk) and unlock some unique applications
To get started you will need a Pinata API Key!
## API Keys
Visit the [Pinata API Keys](https://app.pinata.cloud/developers/api-keys) page to generate new keys.
In the 'New Key' modal, you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, so be sure to give it a name to keep track of it. Once you have that filled out, click "Generate API Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT
again
Once you have created your keys you can go ahead and try testing them! Try to paste this into your terminal with your `JWT`
```bash
curl --request GET \
--url https://api.pinata.cloud/v3/farcaster/users/1 \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see something like this!
```json 200
{
"data": {
"fid": 1,
"custody_address": "0x8773442740c17c9d0f0b87022c722f9a136206ed",
"recovery_address": "0x00000000fcb080a4d6c39a9354da9eb9bc104cd7",
"following_count": 2,
"follower_count": 7945,
"verifications": ["0x86924c37a93734e8611eb081238928a9d18a63c0"],
"bio": "A sufficiently decentralized social network. farcaster.xyz",
"display_name": "Farcaster",
"pfp_url": "https://i.imgur.com/I2rEbPF.png",
"username": "farcaster"
}
}
```
## Endpoints Overview
### [`/interactions`](/farcaster/frame-analytics-api/send-analytics)
This endpoint is used for both sending analytics and fetching them for a set of parameters
#### Send Analytics
Can be any name you want to give your frame to reflect in the analytics results
More granular for specific parts of your frame
An object representing the raw payload of an action frame produced by Farcaster.
```json
{
"untrustedData": {
"fid": 2,
"url": "https://fcpolls.com/polls/1",
"messageHash": "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
"timestamp": 1706243218,
"network": 1,
"buttonIndex": 2,
"inputText": "hello world",
"castId": {
"fid": 226,
"hash": "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9"
}
},
"trustedData": {
"messageBytes": "d2b1ddc6c88e865a33cb1a565e0058d757042974..."
}
}
```
#### Get Interactions
Start date for the analytics results in the format of YYYY-MM-DD HH:MM:SS
End date for the analytics results in the format of YYYY-MM-DD HH:MM:SS
Filters results by a specified `frame_id` used in the initial post request
Filters results by the `button_index` used in the frame payload
Filters results by the `url` of a frame
Filters results by a specified `custom_id` used in the initial post request
### [`/interactions/top`](/farcaster/frame-analytics-api/get-top-interactions)
Instead of fetching the interactions for a specific frame, get results for all frames with analytics in your Pinata account
#### Get Top Interactions
Start date for the analytics results in the format of YYYY-MM-DD HH:MM:SS
End date for the analytics results in the format of YYYY-MM-DD HH:MM:SS
Filters results for a specific attribute. The following are available:
* `button_index`
* `cast_fid`
* `cast_hash`
* `frame_id`
* `url`
# Send Analytics
post /interactions
# Frames
Learn more about Farcaster Frames and how to build them!
Frames introduce a groundbreaking approach to interactive applications within Farcaster. These self-contained applications operate directly in-feed, seamlessly integrating with any Farcaster client. This integration offers developers an unparalleled opportunity for instant distribution of applications or games in a frame format, fostering a surge of creativity and innovation.
## **Key Features of Farcaster Frames**
**Platform Agnostic**
* While the majority of development on Farcaster Frames has been concentrated around JavaScript, the framework is designed to be language-independent. The core requirement for a Frame is the ability to serve HTML content from a server, making it accessible to a wide range of technologies and programming languages.
**Open Graph Standard Compliance**
* Frames leverage the widely-used Open Graph protocol, which utilizes HTML meta tags to convey essential information about web links, including URLs, titles, descriptions, and preview images. Farcaster Frames extend this standard by incorporating additional meta tags for interactive elements such as buttons and endpoint URLs for POST requests. These requests can carry user information and signature data, essential for identity verification and interactive functionalities.
**Endless Possibilities for Interactivity**
* The adaptability of Frames has unlocked a myriad of interactive possibilities. Examples of applications range from ordering services (e.g., [cookies](https://warpcast.com/cookie/0x35bbc5a0)) and [emoji-based chatting](https://warpcast.com/stevedylandev.eth/0x46e3fee2) to playing iconic games [like DOOM](https://warpcast.com/cassie/0xc329ea28) - all within the Farcaster feed. This versatility showcases the potential for creating a diverse array of engaging experiences.
## Frame Analytics
If you are using the [FDK](/farcaster/fdk) and you have linked your Pinata account to your Farcaster account using the [Farcaster Integration](/account-management/farcaster), then you will be able to take advantage of Frame Analytics.
When using the FDK make sure to use `fdk.sendAnalytics` method with your own made up `frame_id` and the `frame_data` sent from the frame.
You can also include an optional `custom_id` param to track and segment specific requests within the specified frame.
```javascript
// This should be the raw payload from the frame action
const frame_data = {
untrustedData: {
fid: 2,
url: "https://fcpolls.com/polls/1",
messageHash: "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
timestamp: 1706243218,
network: 1,
buttonIndex: 2,
inputText: "hello world", // "" if requested and no input, undefined if input not requested
castId: {
fid: 226,
hash: "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9",
},
},
trustedData: {
messageBytes: "d2b1ddc6c88e865a33cb1a565e0058d757042974...",
},
};
const frame_id = "my-unique-frame-name"
const custom_id = "my_custom_id"
await fdk.sendAnalytics(frame_id, frame_data, custom_id)
```
After this is deployed you will see the analytics on the [Frame Analytics Page](https://app.pinata.cloud/frames-analytics).
## Frame Tutorials
Dive deeper into the world of Farcaster Frames with our comprehensive tutorials, designed to cater to a variety of technical backgrounds.
* [How To Build Farcaster Frames In Go](https://www.pinata.cloud/blog/how-to-build-farcaster-frames-in-go)
* [How To Make A Frame On Farcaster Using IPFS](https://www.pinata.cloud/blog/how-to-make-a-frame-on-farcaster-using-ipfs)
* [How to Build a Farcaster Frame that Mints NFTs](https://www.pinata.cloud/blog/how-to-build-a-farcaster-frame-that-mints-nfts)
* [How to Use the Frame Development Kit to Build Farcaster Frames](https://www.pinata.cloud/blog/how-to-use-the-frame-development-kit-to-build-farcaster-frames)
# Fetch all casts authored by an FID.
get /v1/castsByFid
# Fetch all casts by parent cast's FID and Hash OR by the parent's URL
get /v1/castsByParent
# Fetch all casts that mention an FID
get /v1/castsByMention
# Get a cast by its FID and Hash.
get /v1/castById
# Get a list of all the FIDs
get /v1/fids
# Info
get /v1/info
# Get a link by its FID and target FID.
get /v1/linkById
# Get all links from a source FID
get /v1/linksByFid
# Get all links to a target FID
get /v1/linksByTargetFid
# Get a list of on-chain events provided by an FID
get /v1/onChainEventsByFid
# Get a list of signers provided by an FID
get /v1/onChainSignersByFid
**Note:** one of two different response schemas is returned based on whether the caller provides the `signer` parameter. If included, a single `OnChainEventSigner` message is returned (or a `not_found` error). If omitted, a non-paginated list of `OnChainEventSigner` messages is returned instead
# Get an on chain ID Registry Event for a given Address
get /v1/onChainIdRegistryEventByAddress
# Get a reaction by its created FID and target Cast.
get /v1/reactionById
# Get all reactions by an FID
get /v1/reactionsByFid
# Get all reactions to a cast
get /v1/reactionsByCast
# Get all reactions to a target URL
get /v1/reactionsByTarget
# Get an FID's storage limits.
get /v1/storageLimitsByFid
# Get UserData for a FID.
get /v1/userDataByFid
**Note:** one of two different response schemas is returned based on whether the caller provides the `user_data_type` parameter. If included, a single `UserDataAdd` message is returned (or a `not_found` error). If omitted, a paginated list of `UserDataAdd` messages is returned instead
# Get a list of proofs provided by an FID
get /v1/userNameProofsByFid
# Get an proof for a username by the Farcaster username
get /v1/userNameProofByName
# Get a list of verifications provided by an FID
get /v1/verificationsByFid
# Hubs
Hubs are peer to peer servers that work together to download and serve both onchain and offchain data for the Farcaster network. They are key to keeping the network running and for allowing developers to build apps on top of Farcaster. Any time you need to access data from Farcaster such as messages, users, or events, you'll need to access it in the form of a Hub. Sometimes this might take place in the raw [Hub API](/farcaster/api-reference) or through an abstraction.
Pinata offers free to use Hubs that can be accessed via **API**:
```
hub.pinata.cloud
```
Or through **gRPC**:
```
hub-grpc.pinata.cloud
```
Pinata runs its Hubs through a proxy so the standard 2281 port is not required
### Hub API
The Hub API can be used like any other API to fetch data like so:
```bash
curl --request GET \
--url 'https://hub.pinata.cloud/v1/castsByFid?fid=6023&pageSize=10&reverse=true'
```
For more info on how to use the Hub API, check out our [Hub API Reference](/farcaster/api-reference).
### Hub gRPC
The gRPC endpoint can be used inside the [hub-nodejs](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs) library and provides a level of abstraction.
```javascript
import { getSSLHubRpcClient } from '@farcaster/hub-nodejs';
const hubRpcEndpoint = 'hub-grpc.pinata.cloud';
const client = getSSLHubRpcClient(hubRpcEndpoint);
client.$.waitForReady(Date.now() + 5000, (e) => {
if (e) {
console.error(`Failed to connect to ${hubRpcEndpoint}:`, e);
process.exit(1);
} else {
console.log(`Connected to ${hubRpcEndpoint}`);
client.getCastsByFid({ fid: 6023 }).then((castsResult) => {
castsResult.map((casts) => console.log(casts.messages));
client.close();
});
}
});
```
Please consult the [hub-nodejs documentation](https://github.com/farcasterxyz/hub-monorepo/tree/main/packages/hub-nodejs/docs) for more info on how to use it.
# Image Uploads
Leverage Pinata + IPFS to add decentralized image uploads to your Farcaster client
While building a Farcaster client one of the most common things users will request after the ability to send casts is to add images or media to their casts, and thankfully Pinata makes this a breeze! In this doc we'll show you how you can add Pinata IPFS uploads to your app no matter what stack you're using.
The following guide will use Typescript but since this is using the [Pinata API](/api-reference/introduction) you can use whatever language you would like.
## API Keys
The first thing you'll need to do is visit the [Pinata API Keys](https://app.pinata.cloud/developers/api-keys) page to generate an API key.
In the 'New Key' modal, you can choose if you want the key to be an Admin key and have full access over every endpoint, or scope the keys by selecting which endpoints you want to use. You can also give it a limited number of uses, so be sure to give it a name to keep track of it. Once you have that filled out, click "Generate API Key" and it will show you the `pinata_api_key`, `pinata_api_secret_key`, and the `JWT`. It's best to click "Copy All" and keep the API key data safe and secure.
Once API keys have been created, you will not be able to see the secret or JWT
again
Once you have created your keys you can go ahead and try testing them! Try to paste this into your terminal with your `JWT`
```bash
curl --request GET \
--url https://api.pinata.cloud/data/testAuthentication \
--header 'accept: application/json' \
--header 'authorization: Bearer YOUR_PINATA_JWT'
```
If successful you should see this!
```shell bash
{
"message": "Congratulations! You are communicating with the Pinata API!"
}
```
## Adding Uploads to Your Client
There are several approaches you can take to keep your API keys safe but the primary two we reccomend are:
1. Send the file to your owner server via API so the server can upload to Pinata
2. Create a temporary key using [`/users/generateApiKey`](/api-reference/endpoint/generate-pinata-api-key) on the server and upload via the client
In this guide we'll show you second approach as it helps reduce the friction experienced when uploading larger file sizes (e.g. Next.js / Vercel has a limit of 4mb that can be sent via their API routes)
In our server code we'll need a few endpoints with functions that will generate a temporary API key thats only valid for one use, then another to revoke the key just to be extra safe. If you were writing this in an API framework like Hono on a Cloudflare worker it would looke something like this.
```typescript
// GET route to fetch a key
app.get("/key", async (c) => {
try {
const id = uuidv4();
const body = JSON.stringify({
keyName: id.toString(),
permissions: {
endpoints: {
pinning: {
pinFileToIPFS: true
},
},
},
maxUses: 1,
});
const keyRes = await fetch(
"https://api.pinata.cloud/users/generateApiKey",
{
method: "POST",
body: body,
headers: {
accept: "application/json",
"content-type": "application/json",
authorization: `Bearer ${c.env.PINATA_JWT}`,
},
},
);
const keyResJson: any = await keyRes.json();
console.log(keyResJson);
const keyData = {
pinata_api_key: keyResJson.pinata_api_key,
JWT: keyResJson.JWT,
};
return c.json(keyData, { status: 200 });
} catch (error) {
console.log(error);
return c.json({ text: "Error creating API Key:" }, { status: 500 });
}
});
// PUT route to revoke the key
app.put("/key", async (c) => {
const keyId = c.req.query("keyId");
const keyData = JSON.stringify({
apiKey: keyId
});
try {
const keyDelete = await fetch(
"https://api.pinata.cloud/users/revokeApiKey",
{
method: "PUT",
body: keyData,
headers: {
accept: "application/json",
"content-type": "application/json",
authorization: `Bearer ${c.env.PINATA_JWT}`,
},
},
);
const keyDeleteRes: any = await keyDelete.json();
console.log(keyDeleteRes);
return c.json(keyDeleteRes);
} catch (error) {
console.log(error);
return c.json({ text: "Error Deleting API Key:" }, { status: 500 });
}
});
```
In this code we have two endpoints, one for creating and one for revoking. In the creation we make an object with the key permissions, which in this case is just to pin a file and will only work once. After creating it we send back the key and the JWT which we'll see used shortly. Then in the revoking we take that same `pinata_api_key` as the value for `apiKey` to identify what key we will be revoking. That's it!
On the client side code, the functions to create and revoke that key might look something like this.
```typescript
export const generatePinataKey = async () => {
try {
const tempKey = await fetch(`${SERVER_URL}/key`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
},
});
const keyData = await tempKey.json();
return keyData;
} catch (error) {
console.log("error making API key:", error);
throw error;
}
};
export async function deleteKey(keyId: string) {
try {
const deleteKey = await fetch(`${SERVER_URL}/key?keyId=${keyId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
});
const deleteJson = await deleteKey.json();
console.log(deleteJson);
} catch (error) {
console.log("Error deleting API key:", error);
}
}
```
Now that we have a temporary API key on the client, we can upload our file to IPFS from a client form. There are multiple ways to do different types of file uploads where are documented further [here](/pinning/pinning-files#using-the-api), but in short the Pinata API can accept `readableStreams` from a local file system or `blobs`. With the code below we'll do a simple upload where the file is handled via React `useState`.
```typescript
import { useState } from "react";
function App() {
const [selectedFile, setSelectedFile]: any = useState();
const changeHandler = (event: any) => {
setSelectedFile(event.target.files[0]);
};
const handleSubmission = async (keyData: any) => {
try {
const formData = new FormData();
formData.append("file", selectedFile);
const metadata = JSON.stringify({
name: "File name",
});
formData.append("pinataMetadata", metadata);
const res = await fetch(
"https://api.pinata.cloud/pinning/pinFileToIPFS",
{
method: "POST",
headers: {
Authorization: `Bearer ${keyData.JWT}`,
},
body: formData,
}
);
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
return (
<>
>
);
}
export default App;
```
Here we just pass in the `keyData` we got from our server to access our one time use `JWT` and send the request to Pinata. Once the upload is complete we'll get a response that looks like this:
```json
{
"IpfsHash": "bafkreicnpgfq256elalpa6x6avqti3txb6dphsgqekpdkkxq3frjbph3de",
"PinSize": 9719,
"Timestamp": "2024-05-14T18:29:30.541Z",
"isDuplicate": true
}
```
The `IpfsHash` or [CID](/ipfs-101/what-are-cids) is both the identifier and address for our content which we'll access soon in sending a cast.
Now that our content is uploaded, we can access it through our [Dedicated Gateway](/gateways/dedicated-ipfs-gateways) using the following pattern:
```
https://your-gateway-domain.mypinata.cloud/ipfs/:cid
```
Something else you might want to do is use the `?filename=` query at the end of the url to designate a filetype.
You can use the mimetype of the headers to help determine what file extension you want to use! [Check out this example](https://github.com/PinataCloud/farcaster-photo-client-template/blob/2a61d29a01008d764d740d39a510193ea6e51691/utils/upload-files.ts#L44-L56)
Here is an example of a fully functional Gateway URL
```
https://dweb.mypinata.cloud/ipfs/bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4?filename=pinning.png
```
With this full image URL you can add it as a `url` object in the `embeds` array when sending a cast like so:
```json
{
"embeds": [
{
"url": "https://dweb.mypinata.cloud/ipfs/bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4?filename=pinning.png"
}
]
}
```
If you would like to see a fully completed client example using this method check out our open source [Lite Client](https://github.com/PinataCloud/diet-cast) repo!
# Introduction
Farcaster is a decentralized social media protocol that runs on a combination of smart contracts on a blockchain and peer-to-peer servers called “Hubs.” It's similar to other social media where you can have an account, follow other people, make posts, reply, and react to other’s posts. The primary difference is that it is a protocol and decentralized, meaning anyone can build their own interface/client on top of Farcaster. For instance, the most popular is [Warpcast](https://warpcast.com) and acts as the default, but there is also [Supercast](https://supercast.xyz) which has special paid features and [Tiles](https://v0.tiles.cool) that is image focused.
Pinata has been watching and [writing](https://medium.com/pinata/how-to-build-a-video-app-on-farcaster-7e1943fcabe1) about Farcaster for a while now, but more recently has [dedicated more resources](https://www.pinata.cloud/blog/why-we-are-all-in-on-farcaster) to supporting the Farcaster network in the form of free and open Hubs that developers can use, as well as other tools to accelerate apps and Frames. We believe IPFS has the ability to increase the potential of media rich clients building on Farcaster, and we have the goal to make that seamless as possible.
For more information on the Farcaster protocol, check out their docs [here](https://docs.farcaster.xyz). If you’re ready to start building check out our [Hubs page](/farcaster/hubs) or our [Hub API Reference](/farcaster/api-reference)
# Dedicated IPFS Gateways
Dedicated Gateways are the fastest way to fetch content from IPFS, and are the ideal tool when building decentralized applications. When you create a Pinata account, you'll automatically have a Dedicated Gateway created for you! To see it, simply visit the [Gateways Page](https://app.pinata.cloud/gateway).
The gateway domains are randomly generated and might look something like this:
```
aquamarine-casual-tarantula-177.mypinata.cloud
```
## Viewing Content Through Your Gateway
To view content through your gateway, grab the CID of the file you'd like to view and add it to your gateway URL, like so:
Simple as that!
You can also fetch the data programatically using the [get](/ipfs-sdk/gateways/get) method in the [IPFS SDK](/ipfs-sdk).
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = await pinata.gateways.get("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
## Restricted vs Open
All Pinata Dedicated Gateways are restricted by default. This mean they will only serve CIDs that are pinned to your account, which keeps your gateway safe from abuse by outside actors who may want to use it for themselves.
If you plan to upload content to your own account then use your gateway to fetch it, you shouldn't have to touch a thing and it will work as expected! However, lets say you're building a marketplace and you need to fetch content outside your account. In that case, you will want to use [Gateway Access Controls](/gateways/gateway-access-controls). Adding one of these will allow you to access CIDs from the public IPFS network, but it has to meet that access control condition, like a Gateway API Key or Host Origin requirement. Be sure to read our [docs on Gateway Access Controls](/gateways/gateway-access-controls) to learn more.
The only way to open a Dedicated Gateway and allow any CID to go through is to
add a Gateway Access Control
## Convert IPFS Links or Gateway URLs to use Your Dedicated Gateway
If you're a developer building an app to index the blockchain, it's likely you'll encounter several IPFS URL formats that might not be ideal to work with:
```
ipfs://QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng
https://ipfs.io/ipfs/QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng
```
Pinata provides a tool designed to assist you in scenarios like this. It can take IPFS urls, whether they are the protocol standard (`ipfs://`) or another gateway (`https://ipfs.io`), and turn them into your specified Dedicated Gateway. Check it out with the link below!
[IPFS Gateway Tools](https://github.com/PinataCloud/ipfs-gateway-tools)
## Adding a Custom Domain
Pinata also allows you to create a custom domain for your Dedicated Gateway. Simply visit the [Gateways Page](https://app.pinata.cloud/gateway), click the menu button on the right side of your gateway, then click Add Custom Domain. You'll need to own the domain you want to use. When you enter your domain, you will be prompted to enter DNS information through your registrar.
## Billing and Usage
When it comes to using a Dedicated Gateway, there are a few metrics Pinata uses for billing.
| Metric | Description |
| :-------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Bandwidth | Bandwidth is the amount of data that is being going through your Gateway. For instance, if you have a 10MB file go through your gateway 100 times, that would be 1GB of bandwidth used for the month. |
| Requests | A request is anytime content is queried through the gateway, so if you run `wget https://mygateway.mypinata.cloud/ipfs/QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng` in your terminal, that would be one request. Or if you had someone visit your app that uses the gateway on the client side, each time the website requests content from IPFS using the gateway, that's a request. |
Both of these metrics reset on a monthly basis based on your billing cycle.
If you're using a Dedicated Gateway for your NFT project, be sure to check out
[this
guide](https://knowledge.pinata.cloud/en/articles/6461213-token-uris-in-nft-projects)
on how you should do that.
If you have any questions in regards to billing, please don't hesitate to reach out via our chat in the bottom right of our app or [email us](mailto:team@pinata.cloud)!
# Gateway Access Controls
Pinata's Dedicated Gateways make it possible to fetch and serve IPFS content quickly and reliably, however there can be security risks in exposing an open gateway to the world. That's why Pinata has created Gateway Access Controls. These controls will allow you to further limit your gateway, making sure only your platform is using it. This is accomplished with **Gateway Keys, IP Address Restrictions,** and **Host Origin Restrictions.**
## Restricted vs Open
When working with IPFS Gateways the behavior usually falls into two categories: Restricted and Open.
### Restricted Gateway
Restricted means the gateway can only load content that is pinned to that user's account. This is the default behavior for Pinata's Dedicated Gateway, as it provides the maximum level of security. Any requests made to a CID outside the user's account will be unauthorized.
This check of whether or not a file is pinned or not using `/data/pinList` in the Pinata API, which means recursive CIDs in folders are considered unauthorized. Instead, use the folder CID in the path of the gateway followed by the file name.
### Open Gateway
Open means the gateway can access any content on IPFS. An example might be a public gateway like `gateway.pinata.cloud` which are open to everyone, however might have rate limiting to keep them from crashing under heavy usage.
### Why Access Controls?
If someone were to open their Dedicated Gateway without any permissions, anyone who found the domain could use it for themselves and abuse it, leaving the owner with a big bill of overages.
Because of this Pinata developed Gateway Access Controls to allow users to open their Gateways, but with restrictions that must be met first.
## Access Controls
Pinata currently provides three primary methods for opening your gateway securely:
* [Gateway Keys](#gateway-keys)
* [IP Address](#ip-address)
* [Host Origin](#host-origin)
### Gateway Keys
Adding a Gateway Key restriction means that content served through your gateway will only be served successfully if the key is present with the request. **Importantly, content pinned to your account won't be accessible through your gateway if you've implemented a gateway key restriction and fail to include that token in content requests.**
To create a Gateway Key, click on the button that says "Request Key."
When you create a key you will have the ability to preview the token by clicking the "eye" icon, or copy the token to your clipboard with the "copy" icon. At any point, you can delete a gateway key by clicking the "trash" icon.
Once you have the key, there are two ways you can use it in the gateway request.
#### Query Parameter
To use the query parameter method, simply add this to the end of a gateway request url:
```
?pinataGatewayToken=PASTE_IN_GATEWAY_KEY
```
#### Header
Another way to use the gateway key is in the request header. The Key Value would look like this:
| Key | Value |
| ---------------------- | ------------ |
| x-pinata-gateway-token | GATEWAY\_KEY |
**Please keep in mind that using the gateway key in the request header may not work in a client side application, so consider using IP Address restriction instead for those use cases.**
### IP Address
You can also restrict your gateway by IP Address. You can add up to 100 different IP addresses (individually). When you add this restriction, only content requested from an IP address that you've added will be served through your gateway.
To start, click "Set IP Address" on the right side of the menu.
You will get window asking for a valid IP Address which will allow any requests being made from the IP Address to go through!
### Host Origin
With the Host Origin restriction, you can make sure your gateway can only be used on a specific domain (for example, 'app.pinata.cloud'). To get started, click on "Add Host Origin."
After that, you can add the domain you would like your gateway to be used from!
Keep in mind, if you are rendering content on the client side using Host Origins, you will need to include a `crossorigin` tag in your `img`, `video`, `audio`, `link`, or `script` elements. Here is an example with an img element in React:
```javascript html
```
```javascript React
```
For more info on `crossorigin` please read this article [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)!
### Multiple Restrictions
You can add multiple Access Controls, and they will perform as an "OR" operator. This means that if you have Host Origins and a Gateway Key set, you can use either one for content to go through.
# Image Optimizations
Pinata image optimizations provides image optimization functionality directly through your [Dedicated Gateway](). These capabilities that can significantly improve the load time and experience when viewing image content.
Any image you have uploaded can be manipulated with query string parameters. The query string options are defined below.
## Options
At least one option must be specified. Options are comma-separated (spaces are not allowed anywhere). Names of options can be specified in full or abbreviated.
Specifies maximum width of the image in pixels. Exact behavior depends on the fit mode (described below).
Specifies maximum height of the image in pixels. Exact behavior depends on the fit mode (described below).
Device Pixel Ratio. Default 1. Multiplier for width/height that makes it easier to specify higher-DPI sizes in .
Affects interpretation of width and height. All resizing modes preserve aspect ratio. Available modes are:
* `img-fit=scale-down` Image will be shrunk in size to fully fit within the given width or height, but won’t be enlarged.
* `img-fit=contain` Image will be resized (shrunk or enlarged) to be as large as possible within the given width or height while preserving the aspect ratio.
* `img-fit=cover` Image will be resized to exactly fill the entire area specified by width and height, and will cropped if necessary.
* `img-fit=crop` Image will be shrunk and cropped to fit within the area specified by width and height. The image won’t be enlarged. For images smaller than the given dimensions it’s the same as scale-down. For images larger than the given dimensions, it’s the same as cover.
* `img-fit=pad` Image will be resized (shrunk or enlarged) to be as large as possible within the given width or height while preserving the aspect ratio, and the extra area will be filled with a background color (white by default). Transparent background may be very expensive, and it’s better to use fit=contain and CSS object-fit: contain property instead.
When cropping with `fit=cover`, specifies the most important side or point in the image that shouldn’t be cropped off.
* `img-gravity=auto` The point will be guessed by looking for areas that stand out the most from image background
* `img-gravity=side` and `img-gravity=XxY`
If a side (left, right, top, bottom) or coordinates specified on a scale from 0.0 (top or left) to 1.0 (bottom or right), 0.5 being the center. The X and Y coordinates are separated by lowercase x, e.g. 0x1 means left and bottom, 0.5x0.5 is the center, 0.5x0.33 is a point in the top third of the image.
Specifies quality for images in JPEG, WebP and AVIF formats. The quality is in 1-100 scale, but useful values are between 50 (low quality, small file size) and 90 (high quality, large file size). 85 is the default. When using the PNG format, an explicit quality setting allows use of PNG8 (palette) variant of the format.
Allows serving of the WebP format to browsers that support it. If this option is not specified, a standard format like JPEG or PNG will be used.
Reduces animations to still images. This setting is recommended to avoid surprisingly large animGIF files, or flashing images.
Specifies strength of sharpening filter. The value is a floating-point number between 0 (no sharpening) and 10 (max). 1 is a recommended value.
In case of a fatal error that prevents the image from being resized use `img-onerror=redirect` to redirect to the unresized source image URL. This may be useful in case some images require user authentication and cannot be fetched. This option shouldn’t be used if the source images may be very large. This option is ignored if the image is from another domain (subdomains are OK).
* `img-onerror=redirect` Redirects to original source url
Controls amount of invisible metadata (EXIF data) that should be preserved. Color profiles and EXIF rotation are applied to the image even if the metadata is discarded. Note that if the Polish feature is enabled, all metadata may have been removed already and this option may have no effect.
* `img-metadata=keep` Preserve most of the image metadata (including GPS location) when possible.
* `img-metadata=copyright` Discard all metadata except EXIF copyright tag. This is the default for JPEG images. img-metadata=none Discard all invisible metadata.
## Formats and limitations
Read JPEG, PNG, GIF (including animations), and WebP images. SVG is not supported, since this format is inherently scalable and does not need resizing. Resize and generate JPEG and PNG images, and optionally AVIF or WebP. AVIF format is supported on a best-effort basis. Images that cannot be compressed as AVIF will be served as WebP instead.
# Content Addressable
Use the Content Addressable plugin to include CID signatures
The Content Addressable plugin works in tandem with the Signatures API, allowing users to get a signature field in the headers of a Dedicated Gateway request. This can be very useful to help verify content ownership when retrieving files from IPFS.
For more information about adding signatures to CIDs on IPFS please read the [Signatures Guide](/pinning/signatures)
## Installation
Follow the steps in the [Getting Started](/gateways/plugins/getting-started) guide to install the Content Addressable Plugin.
## Usage
Once the plugin is installed the rest is quite simple. If a CID as a signature attached to it, then there will be a `pinata-signature` field in the headers.
```typescript
const signatureReq = await fetch(
`https://.mypinata.cloud/ipfs/`,
{
method: "HEAD",
}
);
const signature = signatureReq.headers.get("pinata-signature");
//0xbe92f9747e1712f2673ff0de6c7058968b869ce14ed6e0949fa3c485e420a27c66695401f56d3d3158fcbb6a5a7809e91acb9660dc00f31673d7efe6f77665b31c
```
# Getting Started
Learn how to use Dedicated Gateway plugins to supercharge your IPFS delivery system
Dedicated Gateways are crucial for retrieving and delivering content on IPFS, but there are times you may need additonal functionality. Gateway Plugins serve that purpose, anything from hot swapping CIDs to delivering signatures and more!
## Installing Plugins
You can install plugins either through the Pinata App or the Pinata API.
### Pinata App
To install a plugin navigate to the Plugins Marketplace tab on the right side.
Once there you can find the plugin you want to install and click "Install." This will bring up a drop down of your Dedicated Gateways to choose which the plugin is installed to.
Once installed you can confirm its there by going to the "My Plugins" tab.
### Pinata API
With the Pinata API you can list all available plugins using the [/ipfs/plugins\_marketplace](/api-reference/endpoint/ipfs/list-marketplace-plugins) endpoint.
```typescript Get Plugins
const plugins = await fetch("https://api.pinata.cloud/v3/ipfs/plugins_marketplace", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PINATA_JWT}`
}
})
```
```json Response
{
"data": [
{
"id": 1,
"name": "Content Addressable Attestation",
"description": "Verify the authenticity and ownership of files added to the IPFS network by signing them with your Ethereum wallet.",
"image": "https://mktg.mypinata.cloud/ipfs/QmVm3jRZ7S2KMmYEvkmkwncRCmBSUEuzNKfm2AgJkfBGKZ",
"tags": [
"verifiability",
"security"
]
},
{
"id": 2,
"name": "Hot Swaps",
"description": "Make IPFS mutable by pointing your original CID to a new file.",
"image": "https://mktg.mypinata.cloud/ipfs/QmVpy6v7YASWUQjmqgUxKwbn6zgzuVSRudzdZMynpBjSJo",
"tags": [
"files"
]
}
]
}
```
To install one you can make a request to [/ipfs/gateway\_plugins/:gateway\_id](/api-reference/endpoint/install-gateway-plugin) by using the [gateway\_id](/api-reference/endpoint/list-gateways) as a path parameter and the `plugin_id` as body of the request.
```typescript Install Plugin
const data = JSON.stringify({
plugin_id: 1
})
const gatewayId = "8673cd80-bf53-4bca-b684-bec1d6bdf004"
const installPlugin = await fetch(`https://api.pinata.cloud/v3/ipfs/gateway_plugins/${gatewayId}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PINATA_JWT}`
},
body: data
})
```
```json Response
{
"data": {
"gateway_id": "8673cd80-bf53-4bca-b684-bec1d6bdf004",
"plugin_id": 1
}
}
```
You can confirm the plugin is installed by using [/ipfs/gateway\_plugins/:gateway\_id](/api-reference/endpoint/list-installed-plugins-for-gateway)
```typescript Get Plugins for Gateway
const gatewayId = "8673cd80-bf53-4bca-b684-bec1d6bdf004"
const installPlugin = await fetch(`https://api.pinata.cloud/v3/ipfs/gateway_plugins/${gatewayId}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PINATA_JWT}`
}
})
```
```json Response
{
"data": {
"gateway_id": "8673cd80-bf53-4bca-b684-bec1d6bdf004",
"plugin_id": 1
}
}
```
## Using Plugins
Each plugin has its own unique use case and usage, please see the appropriate links for each one to see how they are used.
[Content Addressable Plugin](/gateways/plugins/content-addressable)
## Uninstalling Plugins
Just like installing, removing a plugin can be done through the Pinata App or the Pinata API.
### Pinata App
To unintall a plugin first navigate to the Plugin Marketplace tab and then select the "My Plugins" tab.
Then locate the target gateway and plugin, then click the action item on the right hand side and select "Uninstall" from the dropdown menu.
### Pinata API
To uninstall a plug from the API you can use the [/ipfs/gateway\_plugins/:gateway\_id/plugin/:plugin\_id](/api-reference/endpoint/uninstall-gateway-plugin) endpoint, where the `gateway_id` is the target gateway and the `plugin_id` is the target plugin to uninstall.
```typescript Install Plugin
const gatewayId = "8673cd80-bf53-4bca-b684-bec1d6bdf004"
const pluginId = 1
const uninstallPlugin = await fetch(`https://api.pinata.cloud/v3/ipfs/gateway_plugins/${gatewayId}/plugin/${pluginId}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PINATA_JWT}`
}
})
```
# Hot Swaps
Use the Hot Swaps plugin to make one CID map to another
The Hot Swaps plugin can be used to "redirect" a CID to another CID. This can be useful when you need to replace content without updaing the CID hash.
While it may seem like this undermines the benefits of IPFS' imutable content system, it should be made clear that this only works on a per gateway level for those who install it. Additionally:
* Any CID can be called by another gateway to view the original content
* Pinata has an [SDK method](/sdk/gateways/swap-history) and [API endpoint](/api-reference/endpoint/get-swap-history) that any Pinata user can call to check the history of a swap
* All gateway requests that have the hot swap plugin installed and are serving a swapped CID will have a header of `etag` that will have the CID that is currently being directed to.
To demonstrate how this plugin works, consider the following example:
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example.mypinata.cloud", // Gateway has Hot Swaps installed
});
async function main() {
try {
// Upload the first file
const file = new File(["The original CID"], "cid.txt", {
type: "text/plain",
});
const { IpfsHash: CID1 } = await pinata.upload.file(file);
console.log("This is the original CID hash: ", CID1);
// Upload a second file
const file2 = new File(["The new CID"], "cid.txt", { type: "text/plain" });
const { IpfsHash: CID2 } = await pinata.upload.file(file2);
console.log("This is the new CID hash: ", CID2);
// Create the swap, so when we visit CID1 we will get the content of CID2
const swap = await pinata.gateways.swapCid({
cid: CID1,
swapCid: CID2,
});
console.log("Swap created: ", swap);
// Fetch CID1 through our gateway that has Hot Swaps installed, get the content of CID2
const data = await pinata.gateways.get(CID1);
console.log("Result of requestingt CID1 through the gateway: ", data);
} catch (error) {
console.log(error);
}
}
main();
```
## Installation
Follow the steps in the [Getting Started](/gateways/plugins/getting-started) guide to install the Hot Swaps Plugin.
## Usage
After installing the plugin you can then make CID swaps and have them reflect when making Gateway requests. The first parameter `cid` will be the original CID, and `swapCid` will be the content you want it to point to instead.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const swap = await pinata.gateways.swapCid({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
You can fetch the history of CID swaps using the `swapHistory` method, passing in the `cid` of the original CID and the `domain` of the gateway that has the Hot Swaps plugin installed.
```typescript
const history = await pinata.gateways.swapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
// [
// {
// mappedCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
// createdAt: "2024-08-19T14:34:46.492432Z"
// },
// { mappedCid: null, createdAt: "2024-08-19T14:25:10.208726Z" },
// {
// mappedCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu",
// createdAt: "2024-08-19T00:23:41.755206Z"
// }
// ]
```
To delete a CID swap you can simply use the `deleteSwap` method and pass in the CID.
```typescript
const deleteSwap = await pinata.gateways.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
// OK
```
# How does IPFS work with NFTs?
Pinata has been empowering the NFT space since 2018 and there's a good reason for that. IPFS is the perfect pairing for NFTs for several reasons.
* **IPFS is Immutable** - Anything that is uploaded to IPFS cannot change, which helps preserve the value of an NFT.
* **IPFS is Decentralized** - Instead of using a centralized server where one person can control the content, IPFS is distributed and makes sure anyone can pin the content and keep it persisted.
* **IPFS is Portable** - Since the [CID](/ipfs-101/what-are-cids) for content will be constant, and IPFS pinning works in a decentralized manner, anyone can take a CID and pin it themselves. This allows content to be "transferred" and kept up on the network as people value it. (read more about that concept [here](https://medium.com/pinata/who-is-responsible-for-nft-data-99fb4e8147e4))
One of the biggest reasons you want to use IPFS for NFTs is to prevent tampering or "rug pulls" where someone can just delete the data for an NFT and make it worthless. NFTs are tokens on the blockchain that have a "Token URI" which is simply a link pointing to data about that NFT off-chain, because putting data on-chain is far too expensive. If this link is a centralized server, like `https://server.com/pinnie.png`, then whoever has control of the server can simply upload totally different content and keep the same name, thus keeping the same link. Or they could just delete it and it would be empty!
This is where IPFS becomes necessary. Since the address or link to the content on IPFS is the [CID](/ipfs-101/what-are-cids), which is based on the content itself and is immutable, you can't change it or alter it. In addition, the ability for multiple people to [pin](/ipfs-101/what-is-ipfs-pinning) content and help it persist on the network makes it harder for something to just be deleted.
## How to Make an NFT with Pinata
It's important to note that Pinata is currently not providing any minting services. This means you would use Pinata to host the media content and the metadata for the NFT, and then another service or self-deployed smart contract to actually mint the NFT. But don't worry, we'll show you a few different ways you could do that!
### Step 1: Upload the Content
The first thing you need to do is upload the content to Pinata. Since IPFS supports any kind of file, the truth is any kind of file can be an NFT. How that file is referenced in the metadata is a bit of a different story, so be sure to check [metadata standards](https://docs.opensea.io/docs/metadata-standards) to make sure you content will be seen on marketplaces or wallets.
To upload the content you can either do a simple upload through the Pinata App by navigating to the [files page](https://app.pinata.cloud/pinmanager) and uploading through the UI like so:
Or you can upload through the [SDK](/ipfs-sdk) using a script like this:
```typescript
const { PinataSDK } = require("pinata")
const fs = require("fs")
const { Blob } = require("buffer")
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud"
})
async function upload(){
try {
const blob = new Blob([fs.readFileSync("./path/to/image.png")]);
const file = new File([blob], "image.png", { type: "image/png"})
const upload = await pinata.upload.file(file);
console.log(upload)
} catch (error) {
console.log(error)
}
}
```
In each case you will want to grab the CID for that file, which will look something like this:
### Step 2: Create and Upload Metadata
Now that we have the CID for our content on IPFS, we need to create a metadata file that will have all the other information about the NFT. You will want to use a JSON format and follow [industry metadata standards](https://docs.opensea.io/docs/metadata-standards) to make sure that it will show up in marketplaces and wallets. You can use the template below as we'll walk through each piece.
```json metadata.json
{
"name": "Name of NFT",
"description": "Description of NFT",
"external_url": "https://pinata.cloud",
"image": "ipfs://CID_GOES_HERE"
}
```
* `name` - This will be the name of this particular NFT, not the collection.
* `description` - Describes the NFT.
* `external_url` - A link to the website of the NFT project or creator.
* `image` - This would be the link to the image, if it was a video or gif then you would want to follow metadata standards and have a backup image, and also add an `animation_url` for the video.
You'll notice that we are using the IPFS protocol URL for the image link. There are other ways to reference CIDs in NFT metadata which you can read about more [here]().
Once you have that file filled out you will want to save it as something like `metadata.json` (this might be different if you are making a large project using folders). Then you can upload the metadata file to Pinata using the app like before, or if you are using the API we have a [JSON endpoint](/api-reference/endpoint/pin-json-to-ipfs) you can use to simplify the process, like so:
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
external_url: "https://pinata.cloud"
})
```
After the metadata is uploaded you should now have a CID for that metadata file; this will be the core identity of your NFT and will act as the **Token URI**.
### Step 3: Mint the NFT
Now that you have the all important Token URI metadata CID, you can mint an NFT! How you go about doing this will depend on multiple factors.
#### Starting an NFT Project
If you are non-technical and you're just looking for the easy way to create an NFT project, you will likely want to use a minting service like [Bueno](https://bueno.art), [Mintplex.xyz](https://mintplex.xzy), or [Manifold](https://manifold.xyz). There are a lot of complexities with smart contracts and making sure the content is setup properly, and using a service like this will make things much easier!
#### Just Exploring NFTs
If you want to get your feet wet with smart contracts you could follow the video below! It covers the Base layer 2 blockchain in particular, but the method will work on other EVM chains.
#### Implementing into an Decentralized App
If you are a developer that is trying to do NFT minting on a larger scale, you could use an NFT minting API like [Crossmint](https://crossmint.com). Lucky for you we have some tutorials on how to do just that!
# What are CIDs?
Content Identifiers (CIDs) are one of the most important parts of IPFS. Whenever you share a file through IPFS, that file or folder gets assigned a CID that looks like this:
What's happening behind the scenes: IPFS is breaking the file into blocks and running it through a cryptographic hash. It's like a math equation, where if you had `x + 2 = y`, and then you pass in `x = 2`, then `y = 4`. No matter how many times you run it, you will have the same answer if you keep passing in `x = 2`. In the same way, IPFS creates CIDs for files that are reproducible. If you share a file of `Pinnie.png` you will get the same CID every time. It is through this CID that content is fetched across the IPFS network, which each piece pointing to a blocks on different nodes, altogether building a cohesive piece of content.
The CID is determined by the content of the file or folder
Files can be retrieved either with a local IPFS node using a URL like `ipfs://{CID}` or if you don't have an IPFS node, you can use an [IPFS Gateway](/ipfs-101/what-are-ipfs-gateways) like `https://gateway.pinata.cloud/ipfs/{CID}`. This simple concept of CIDs give them several super powers.
### Verifiability
The CID's uniqueness and dependence on the content it represents make the data inherently verifiable. Suppose a creator shares an image of a digital artwork on the IPFS network: if someone tries to replace the original image with a different one, the CID for the new image will be different. This guarantees that the content cannot be tampered with, reinforcing trust within the network.
### Portability and Addressability
CIDs also facilitate data portability. Because a CID represents the content itself rather than its location, you can move your data across various nodes within the IPFS network without losing access to it. This content-addressed system breaks away from the traditional location-based addressing used in systems like HTTP, leading to a more robust and reliable data sharing network.
### Ownership and Persistence
The CID's capabilities don't stop at verification and portability. They also empower users to take ownership of data they care about. By 'pinning' a CID to a pinning service or their own node, users can ensure the longevity of data, even if the original data owner stops hosting it. This is particularly valuable in the context of non-fungible tokens (NFTs). For instance, if you purchase an NFT, you can pin the CID of the token's metadata and image to a pinning service or a node you control, securing its existence indefinitely. This practice truly underlines the principle of digital ownership.
### Community Preservation
In addition to ownership and persistence, communities can preserve digital content through the practice of pinning much like how traditional art is preserved in museums. CIDs provide an opportunity for groups to maintain access to valuable data, ensuring its longevity.
## V0 & V1
There are two different types of CIDs you might see while using IPFS. The first is V0, which looks something like this:
`QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng`
This was the first kind of IPFS CID that was used for several years, however over time a new version was developed called the V1. This version was more flexible and future proof, and looks something like this:
`bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4`
Both of these CIDs are the same content, a wonderful picture of Pinnie, however the cryptographic algorithm they were put through was different. Pinata has used the V0 for several years but now uses V1 for the previously mentioned reasons.
## How Do I Reference CIDs?
When it comes to providing an IPFS link, there are a few options to choose from.
### IPFS Protocol URL
An IPFS protocol URL looks like this:
If you copy and paste that into your browser, you may not get anything back. That is because in order to use this one, you have to have a local IPFS node running to participate in the network. Even when you do, it will likely be very very slow since IPFS is still a growing network.
Why use it? Well for a couple of reasons. If you are building on a blockchain that already uses IPFS a lot, like Ethereum or another L2 chain, lots of marketplaces and apps are used to seeing this format. When they see it, they use tools to convert the url into a gateway url so it can display the content on a website. This can be a good thing or a bad thing. If the platform has a dedicated/private gateway, the speed will be very fast (much like our own dedicated gateways). However, if the platform uses a public gateway, the speeds will be very slow. In the end, the platform has control over how well your content is received. Additionally, using the standard IPFS url might help future proof your assets, as public gateways might be stopped down the road (however the CID is still in the url in those cases, so if the platform knows what to do, they can still get the content if pinned).
If you are on a different L1 chain, you may want to test it first.. There are some platforms on other blockchains that expect "https" instead and nothing will load if you use this.
### Public IPFS Gateway URL
A public gateway URL looks something like this:
This will deliver the content in the browser without the need of a local IPFS node. However, since this gateway is a public gateway, your speed might vary due to the heavy traffic and congestion. Some platforms will see this kind of the url and switch it out with their own faster gateway choice, but not always. Generally you want to assume that if you take this path, the assets will be slow.
### Dedicated IPFS Gateway URL
A [Dedicated Gateway](/gateways/dedicated-ipfs-gateways) URL looks something like this:
Dedicated Gateways are much much faster than any other method, and should ideally be used when trying to display content on your own platform. However, using them in NFTs should be done in caution. If you use a Dedicated Gateway in your NFT project metadata and image links, your speed will be great, however anytime another marketplace or rarity bot asks the blockchain for the IPFS data, your gateway will be hit. Since most Dedicated Gateways are paid services, this could greatly drive up your costs and usage. You'll get the best performance, control, and flexibility with this method, however you might have to pay more than the other methods.
# What are IPFS Gateways?
Once you have uploaded content to Pinata, naturally the first thing you want to do is view it on the IPFS network! But there's a problem: IPFS is a separate protocol, just like HTTP for regular websites is a protocol. To access that content, we need a gateway to bridge IPFS and HTTP. IPFS gateways help us do exactly that!
An example of accessing content from this gateway can be seen with: `https://gateway.pinata.cloud/ipfs/bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4`
What's going on here exactly? Let's break it down. First you have the gateway domain. There are lots of different domains out there, like `ipfs.io`, `gateway.pinata.cloud`, and if you have a Pinata account you get a Dedicated Gateway which might look something like `aquamarine-casual-tarantula-177.mypinata.cloud`. Each of these can be used to bridge files from IPFS to HTTPs. Next you have the IPFS path which looks like `ipfs/` and this is necessary for a gateway to work. Finally we have the CID at the end, which is the actual IPFS address for our content.
## Folder Paths
Sometimes your CID might be a folder, in which case you might have difficulty loading it through the gateway. This happens because if you do not designate a complete folder path, then the gateway will try to load all of the files and index them into a sheet showing every file. This can be pretty intensive depending what kind of gateway you're using, and will likely stall out due to how long it can take. To remedy this, simply add on the file path of the content you're trying to get inside. For instance, if we have a folder with the CID of `QmWfHgs3nKiyFWx3tFEYvm8DiHTrCsxEHxvDdBh95ZQSLT` and the inside looks something like this:
Then we could access the files inside by adding on `/pinnie.png` or `clouds.json` to the end of our folder path. In the end we would have something like this.
## Public Gateways
The most common kind of IPFS Gateways are Public Gateways. These are usually run and maintained by IPFS Pinning Services, protocols, or even smaller groups that want to help build the IPFS ecosystem. They're referred to as "Public" because anyone can access them! You just have to add a CID to the end of one to start using it. You can try that now by adding this CID `bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4` to the end of Pinata's Public Gateway:
```text
https://gateway.pinata.cloud/ipfs/
```
There's something you might notice when you start using Public Gateways frequently, and that is the speed and rate limits. Since these are Public, they're used **heavily** by people all around the world. They are constantly getting hammered, and because of that they're just naturally slower. You can think of it like a highway or interstate: if you have a high flow of traffic, its likely going to bottleneck and cause a traffic jam. Same goes for IPFS Gateways!
Public Gateways are not meant for production apps, be sure to only use them for testing!
## [Dedicated Gateways](/gateways/dedicated-ipfs-gateways)
Thankfully Pinata has you covered! When you sign up for a free Pinata account, you get your own Dedicated Gateway. Dedicated Gateways are like the toll roads on highways and interstates; it's your own private boulevard to get unmatched speeds. Our Dedicated Gateways are well known in the industry as being fast, reliable, and just plain simple to use, and they do that through a large network of IPFS nodes and a built in global CDN that helps cache content to be much faster on subsequent loads. Check on the next few pages to learn more about how they work!
# What is IPFS?
[IPFS](https://ipfs.io) (Inter-Planetary File System) is a peer-to-peer distributed file system that is used primarily for data that can't be stored on a blockchain. Unlike HTTP, a more standard file sharing protocol, IPFS has special properties that make it ideal for a decentralized data model that works in tandem with blockchains. Some of those abilities include:
* **Distributed** - IPFS is made of multiple IPFS nodes, so not one entity can control what is shared on the network.
* **Immutability** - Content that is shared on IPFS cannot be changed or altered.
* **Addressability** - Content that is shared on IPFS uses unique addresses to locate content on the network.
* **Portability** - Data that is pinned to IPFS can be re-pinned or transferred to other nodes, helping valued content persevere on the network.
* **Garbage Collection** - The way IPFS handles data works in such a way that any content that is not pinned by a node will eventually fall off the network, keeping it clean from unwanted data and clutter.
Due to these properties it is ideal for Web3 data storage such as NFTs, DeFi, and other decentralized infrastructures.
## How Does IPFS Work?
The best way to understand IPFS is to see it as an alternative to HTTP. With HTTP server model the end user has a computer which makes a request to see content from a server, then that server sends back the requested content. This is a simple two way communication, and while there are more complex instances, the key is that the data is stored on the server and has ultimate control of that content.
IPFS on the other hand, is a network is made up of a multitude of different IPFS nodes which share data with each other. Whenever a node wants to share content on IPFS, it runs the content through a cryptographic hash and returns a [CID](/ipfs-101/what-are-cids), or "Content Identifier." This CID serves as both the address and the verification that the content is what it says it is. After the CID has been created the IPFS node will "pin" that content to the network, essentially sharing it and saying it is worth keeping.
As the CID is requested by other IPFS nodes, the content will pass through other nodes leaving a cache that can be used for faster retrieval the more it is requested. This cache will eventually get pretty big and can cause bloat, and this is where pinning really comes into play. The IPFS node will run a garbage collector from time to time which will clear the contents of the cache, except for any CIDs that are actively being pinned by another IPFS node. As long as one IPFS node is pinning that content, it will stay on the network. If the content is unpinned and there is no other node holding it, eventually the content will fade from the network. This creates a model that can persevere data but not to a point that will cause clutter and bloat.
## Why IPFS?
Blockchains have an incredible ability to keep a long ledger of transactions and who sends or signs them, however blockchains are terrible at storing data. Since blockchains require gas fees to be paid anytime data is stored on the blockchain, larger forms of data can cause astronomical prices. As of 2023, storing 1GB of data on Ethereum would cost around \$250,000,000! This caused a problem in particular for NFTs as they were first taking off.
NFTs, or really anything being used on the blockchain, will usually have a token element that is on chain that has a pointer to data off-chain (referred to as the token URI). This pointer is usually a link to a metadata file in the form of JSON, which has information like the name of the NFT, a description, and usually another link to an image that is also off-chain. Back in the earlier days of NFTs these pointers or token URIs were centralized servers, and that caused a problem. There was nothing stopping the owner of that server from changing the data to be something completely different. A link like `https://drive.google.com/nft/pinnie.png` could be dynamic; at any time the owner of that file could upload a picture of something else and name it `pinnie.png` and the link would still be the same. What was the point of buying a piece of art on the blockchain if it could change and be worthless?
This is where IPFS steps in, along with it's special abilities of cryptography and distributed nature. Whenever a file is stored on IPFS, it gets a unique CID that is based on the content of the file. If the file is changed by a single pixel or character, then it will receive a completely different CID. It solves the problem of `pinnie.png` being replaced with something else since you couldn't simply just change the name. Due to the cryptographic nature of IPFS, anything shared on IPFS is immutable and cannot be changed.
Since IPFS is decentralized in the way it stores the data, it also solved the potential crisis of something deleting the data or the server going offline. With IPFS pinning, multiple nodes can pin data to ensure that it stays online. A good metaphor for this is a fridge. If you wanted to put a picture on a fridge, you would likely use a magnet to put it up there. If you have two or three magnets on that picture, you could remove the original magnet and the picture would stay up. IPFS works in a similar way; anyone can take the CID for some content and pin it themselves to help preserve it. Content can persist as long as a single IPFS node is pinning it!
## What Does Pinata Do?
Pinata is an IPFS Pinning Service that provides user with, you guessed it, IPFS services! These range from uploading files and pinning them to IPFS, to blazing fast [Dedicated Gateways](/gateways/dedicated-ipfs-gateways) which come in handy if you need to fetch content from IPFS. Our focus is to provide developers easy to use tools so they can add in IPFS to their Web3 architecture as simply as possible, and to make it reliable and trustworthy. If you're not sure where to start, check out our [Getting Started](/quickstart) page which will have you up and running with Pinata in no time! 🚀
# What is Pinning?
IPFS "Pinning" is the foundation to getting content on IPFS. When you upload a file to IPFS, the IPFS node will create a [CID](/ipfs-101/what-are-cids) for that file which will act as the identifier and the address. Then it will "pin" that file to the IPFS network, making it available for other nodes to request it. At that point any other IPFS node can request the content for a CID, and the content will pass through other nodes leaving a cache on that node. This makes it faster to fetch files again if those nodes are used.
As the cache on these nodes through, it will soon become bloated with too much data, similar to how your computer can get slow if it's loaded down with too many cache files. IPFS nodes have a way to handle this though, and that's through something called "Garbage Collection." This is a process where the IPFS node will dump any content in the cache that is not being pinned to IPFS. However if there is content in the cache that is still being pinned by at least one IPFS node, then it will stay. Pinning is what keeps content on IPFS, and as long as content is being pinned by one node, it will stay available on the network.
## How Do I Pin Files to IPFS?
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 [IPFS SDK](/ipfs-sdk) or [IPFS API](/api-reference/endpoint/ipfs/pin-file-to-ipfs). These make it simple to upload files or raw JSON to IPFS!
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: "PINATA_JWT",
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = new File(["Hello IPFS!"], "hello-world.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
```
Check out the [Quickstart](/quickstart) guide to upload your first file
through the SDK!
We also have other tools like the [Pinata CLI](/tools/pinata-cli) or [Next.js Starter](/tools/nextjs-starter) which can be used to upload using [API Keys](/account-management/api-keys).
### Web App
If you're non-technical you can use [Pinata App](https://app.pinata.cloud/pinmanager) 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, selecting your file, give it a name, then upload. Once its complete, you'll see it listed in the files page!
Start uploading by [signing up for a free
account](https://app.pinata.cloud/register)!
### 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](/api-reference/endpoint/pin-by-cid) endpoint with a code snippet like this:
```typescript
import { PinataSDK } from "pinata";
const pinata = new PinataSDK({
pinataJwt: "PINATA_JWT",
pinataGateway: "example-gateway.mypinata.cloud",
});
const pin = await pinata.upload.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
# Deleting Files
The process of removing files from IPFS is called unpinning. When you unpin something from an IPFS storage node, it is marked for garbage collection. When garbage collection runs, the content is permanently deleted from the storage node.
Keep in mind that if someone else has pinned your content on IPFS, it will
remain available even if you delete your files!
## Deleting Programatically
The SDK has a very simple [unpin](/ipfs-sdk/data/unpin) method that will allow you to delete an array of `cids`.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const unpin = await pinata.unpin([
"bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
"QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"
])
```
### Deleting All Files
If you find yourself in a place where you need to unpin a lot of files or perhaps all your files, you can use a script like this to create an array of CIDs and unpin them one by one. The example below uses the [`pinList`](/api-reference/endpoint/list-files) queries to target all pinned files and return 1000 for each request. This could easily be done with a different query to target different files, please check out the [listing files](/pinning/listing-files) doc for more info.
```javascript Unpin All Files
const PINATA_JWT = "YOUR_JWT_HERE";
const PIN_QUERY = `https://api.pinata.cloud/data/pinList?status=pinned&pageLimit=1000&includeCount=false`;
const fetch = require("node-fetch");
const wait = (milliseconds) => {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
};
const fetchPins = async () => {
try {
console.log("Fetching pins...");
let pinHashes = [];
let pageOffset = 0;
let hasMore = true;
while (hasMore === true) {
try {
const response = await fetch(`${PIN_QUERY}&pageOffset=${pageOffset}`, {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${PINATA_JWT}`,
},
});
const responseData = await response.json();
const rows = responseData.rows;
if (rows.length === 0) {
hasMore = false;
}
const itemsReturned = rows.length;
pinHashes.push(...rows.map((row) => row.ipfs_pin_hash));
pageOffset += itemsReturned;
await wait(300);
} catch (error) {
console.log(error);
break;
}
}
console.log("Total pins fetched: ", pinHashes.length);
return pinHashes;
} catch (error) {
console.log(error);
}
};
const deletePins = async () => {
const pinHashes = await fetchPins();
const totalPins = pinHashes.length;
let deletedPins = 0;
try {
for (const hash of pinHashes) {
try {
const response = await fetch(
`https://api.pinata.cloud/pinning/unpin/${hash}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${PINATA_JWT}`,
},
}
);
await wait(300);
deletedPins++;
process.stdout.write(`Deleted ${deletedPins} of ${totalPins} pins\r`);
} catch (error) {
console.log(error);
}
}
console.log("Pins deleted");
} catch (error) {
console.log(error);
}
};
deletePins();
```
## Deleting by Web App
If you are trying to delete files (or in IPFS terms "Unpin"), you can do so by clicking on the "more" button and selecting "Unpin File”
Additionally, with our Bulk File Actions tool, you can select and manage multiple files at once - up to 100!
# Groups
Groups allow you to organize your Pinata content through the Pinata App or through the Pinata API, giving you a clearer picture of what your files are being used for.
## Pinata API
With the [SDK](/ipfs-sdk/groups), you can create groups, add files to groups, list details about a group, and more! You can also mange groups using the [IPFS API](/api-reference/endpoint/ipfs/create-group).
### Create a Group
To create a group you can use the [create](/ipfs-sdk/groups/create) method and passing in the `name` you want to give a group.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.create({
name: "My New Group",
});
```
This will return the Group info
```json
{
"id": "61a4a882-1591-462e-bcb7-fa0eee5f3c51",
"user_id": "ec50a085-a746-428d-b01d-167ac379fbd4",
"name": "NFT-Project",
"updatedAt": "2024-07-03T18:09:33.610Z",
"createdAt": "2024-07-03T18:09:33.610Z"
}
```
### Get a Group
To fetch details of an already existing group you can use the [get](/ipfs-sdk/groups/get) and pass in the `groupId`.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
This will return the same group info received upon creation.
```json
[
{
"id": "61a4a882-1591-462e-bcb7-fa0eee5f3c51",
"name": "NFT-Project-updated",
"user_id": "ec50a085-a746-428d-b01d-167ac379fbd4",
"createdAt": "2024-07-03T18:09:33.610Z",
"updatedAt": "2024-07-03T18:15:17.440Z"
}
]
```
### List All Groups
If you want to get all Groups or filter through them, you can use the [list](/ipfs-sdk/groups/list) method.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups
.list()
.name("Greetings");
```
Results can be filtered with the following queries.
Filter by name of the group
Paginate through next series of groups
Limit the number of results
This will return an array of Groups and their respective info:
```json
[
{
"id": "61a4a882-1591-462e-bcb7-fa0eee5f3c51",
"name": "NFT-Project-updated",
"user_id": "ec50a085-a746-428d-b01d-167ac379fbd4",
"createdAt": "2024-07-03T18:09:33.610Z",
"updatedAt": "2024-07-03T18:15:17.440Z"
}
]
```
### Listing Files in a Group
To list all the files that are part of a group you can use the [listFiles](/ipfs-sdk/data/list-files) method and the `group` filter.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const files = await pinata
.listFiles()
.group('648767d5-0a80-4b3a-9669-93693e4a0519')
```
This will return an array of [PinListItem](/ipfs-sdk/types#pinlistitem) objects.
```json
{
"rows": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ipfs_pin_hash": "",
"size": 123,
"user_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"date_pinned": "2023-11-07T05:31:56Z",
"date_unpinned": "2023-11-07T05:31:56Z",
"metadata": {
"name": "",
"keyvalues": {}
},
"regions": [
{
"regionId": "",
"currentReplicationCount": 123,
"desiredReplicationCount": 123
}
],
"mime_type": "",
"number_of_files": 123
}
]
}
```
### Updating the Name of a Group
You can update the name of a group using the [update](/ipfs-sdk/groups/update) method and passing in the `groupId` and the new `name` you want to use.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
name: "My New Group 2"
});
```
This will return the updated Group info.
```json
{
"id": "3778c10d-452e-4def-8299-ee6bc548bdb0",
"name": "My New Group 2",
"user_id": "ec50a085-a746-428d-b01d-167ac379fbd4",
"createdAt": "2024-07-03T18:09:33.610Z",
"updatedAt": "2024-07-03T18:12:46.930Z"
}
```
### Add CIDs to a Group
At this time, CIDs can only belong to one group at a time. If a CID is added
to a Group while already being part of another, it will move the CID to the
latest requested Group.
After a Group is created you can add CIDs to it using the [addCids](/ipfs-sdk/groups/add-cids) method. Just pass in the `groupId` and an array of `cids`.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.addCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
cids: ["QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"],
});
```
If successful, the endpoint will return an `OK` response.
### Removing CIDs from a Group
To remove CIDs from a Group, you would follow the same pattern as `addCids` except using the [removeCids](/ipfs-sdk/groups/remove-files) method instead.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.removeCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
cids: ["QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"],
});
```
If successful, the endpoint will return an `OK` response.
### Delete a Group
Deleting a Group that has CIDs inside of it will not unpin/delete the CIDs. Please use the [unpin](/ipfs-sdk/data/unpin) method to actually delete a file from your account
To delete a Group you can use the [delete](/ipfs-sdk/groups/delete) method and pass in the `groupId`.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
If successful, the endpoint will return an `OK` response.
## Pinata App
If you just need to organize your files through a web interface, then the Pinata Web App can do just that!
### Create a Group
Navigate to the Groups tab, click "New" in the top right corner, give it a name, then click "Create." If you open the new Group in the web app, then the Group ID is in the URL path.
### Add Files to Groups
At this time, CIDs can only belong to one group at a time. If a CID is added
to a Group while already being part of another, it will move the CID to the
latest requested Group.
There are a few ways you can add files to a Group after you have created one.
#### Upload Directly
After creating a group, you can click on it, then click the "Add" button in the top right to add files directly to the group.
#### File Details
If you already have files uploaded and you just need to add it to a Group, you can click on the file details button on the far right and select "Add to Group"
#### Bulk Add
To add lots of files at once, you can select multiple files from the Files page then click the "Add to Group" button from the toolbar.
### Remove Files from Group
While inside the Group view, you can remove files from a group by either clicking on the file detail for a single file or by using the multi-select tool.
### Deleting Groups
Deleting a Group that has CIDs inside of it will not unpin/delete the CIDs.
Please follow the [unpin guide](pinning/deleting-files) to actually delete a
file from your account
To delete a Group, navigate to the Groups page, click on the details of the Group, and select the "Delete Group" from the dropdown.
# Listing Files
Learn how to list files inside your Pinata account
To list the files on your account you can either use the [SDK](/ipfs-sdk/data/list-files) or the [Pinata API](/api-reference/endpoint/ipfs/list-files) to fetch file data programatically.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const files = await pinata.listFiles().name("pinnie")
```
## Filters
### name
* Type: `string`
Name of the file
```typescript
const files = await pinata
.listFiles()
.name('pinnie')
```
### cid
* Type: `string`
CID of the file
```typescript
const files = await pinata
.listFiles()
.cid('rbafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4r')
```
### group
* Type: `string`
Filter by `groupId`
```typescript
const files = await pinata
.listFiles()
.group('648767d5-0a80-4b3a-9669-93693e4a0519')
```
### keyValue
* Type: `object`
Filter files by `keyValue` objects used in Pinata Metadata
```typescript
const files = await pinata
.listFiles()
.keyValue("whimsey", 100)
```
This also includes an optional `operator` for even more fine tuned data
Type:
```typescript
operator?:
| "gt" // Greater than
| "gte" // Greater or equal to
| "lt" // Less than
| "lte" // Less than or equal to
| "ne" // Not equal
| "eq" // Equal
| "between"
| "notBetween"
| "like"
| "notLike"
| "iLike"
| "notILike"
| "regexp"
| "iRegexp";
```
```typescript
const files = await pinata
.listFiles()
.keyValue("whimsey", 400, "lt")
```
### pageLimit
* Type: `number`
Determine the number of results, default is `10`, max is `1000`
```typescript
const files = await pinata
.listFiles()
.pageLimit(100)
```
### pageOffset
* Type: `number`
Offset the number of files returned to paginate
```typescript
const files = await pinata
.listFiles()
.pageOffset(100)
```
### pinStart
* Type: `string`
Filter by start date in ISO8601 format
```typescript
const files = await pinata
.listFiles()
.pinStart('2024-07-16T11:41:19Z')
```
### pinEnd
* Type: `string`
Filter by end date in ISO8601 format
```typescript
const files = await pinata
.listFiles()
.pinEnd('2024-07-16T11:41:19Z')
```
### pinSizeMin
* Type: `number`
Filter by minimum file size in bytes
```typescript
const files = await pinata
.listFiles()
.pinSizeMin('100000')
```
### pinSizeMax
* Type: `number`
Filter by maximum file size in bytes
```typescript
const files = await pinata
.listFiles()
.pinSizeMax('1000000')
```
## Auto Paginate
The `listFiles` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.listFiles().name("pinnie") {
console.log(item.ipfs_pin_hash);
}
```
Works like magic ✨
## Advance Querying with the API
You also have the option to query your content on the associated metadata that may have been included with the content when it was uploaded.
These queries look very similar to the default parameters but are slightly more complex. Here are few simple examples, with added explanation afterward:
To query on the name you provided for your pin, your query would take this form:
```
?metadata[name]=exampleName
```
(This will match on names that contain the string of characters provided as a value. For added specificity, please include the full name you're trying to find).
To query on the metadata key-value attributes:
```
?metadata[keyvalues]={"exampleKey":{"value":"exampleValue","op":"exampleOp"}}
```
OR
```
?metadata[keyvalues][exampleKey]={"value":"exampleValue","op":"exampleOp"}
```
To query on both the metadata name and multiple key-value attributes:
```
?metadata[name]=exampleName&metadata[keyvalues]={"exampleKey":{"value":"exampleValue","op":"exampleOp"},"exampleKey2":{"value":"exampleValue2","op":"exampleOp2"}}
```
### Explaining the "value" and "op" key / values
As seen above, each query on custom values takes the form of an object with a "value" key and an "op" key.
The "value" is fairly straightforward. This is simply the value that you wish your query operation to be applied to. These values can be:
### Strings
Numbers (integers or decimals)
Dates (Provided in ISO\_8601 format)
The `op` is what query operation will be applied to the value you provided. The following opcodes are available to query with:
`gt` - (greater than)
`gte` - (greater than or equal)
`lt` - (less than)
`lte` - (less than or equal)
`ne` - (not equal to)
`eq` - (equal to)
`between` - (When querying with the 'between' operation, you need to supply a 'secondValue' to be consumed by the query operation)
For Example:
```
?metadata[keyvalues]={"exampleKey":{"value":"2018-01-01 00:00:00.000+00","secondValue":"2018-02-01 00:00:00.000+00","op":"between"}}
```
`notBetween` - (When querying with the `notBetween` operation, you need to supply a `secondValue` to be consumed by the query operation)
For Example:
```
?metadata[keyvalues]={"exampleKey":{"value":4.00,"secondValue":5.50,"op":"notBetween"}}
```
`like` - (you can use this to find values that are similar to what you've passed in)
For example, this query would find all entries that begin with "testValue". A % before your value means anything can come before it, and a % sign after means any characters can come after it. So %testValue% would contain all entries containing the characters "testValue".
```
?metadata[keyvalues]={"exampleKey":{"value":"testValue%","op":"like"}}
```
`notLike` - (you can use this to find values that do not contain the character string you've passed in)
`iLike` - (The case insensitive version of the "like" opcode)
`notILike` - (The case insensitive version of the "notLike" opcode)
`regexp` - (Regular expression matching)
`iRegexp` - (Case insensitive regular expression matching)
# Metadata & Options
For all operations that result in a file being uploaded or added to an account, there are optional pieces of data that can be included.
This primarily applies to the [Pinata API](/api-reference/introduction) as these attributes have been abstracted in the [SDK](/ipfs-sdk).
## `pinataMetadata`
This is not to be confused with NFT metadata, as this metadata about files uploaded to Pinata is not on IPFS but a private database with Pinata
In addition to pinning your file to Pinata, you also have the option to include metadata that is kept with Pinata that can be used for your own organization purposes.
This metadata can later be used for easy querying on what you've pinned with our [pinList](/pinning/listing-files) request.
The optional metadata object takes the following form:
```json
{
"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"
}
}
```
## `pinataOptions`
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](https://flyingzumwalt.gitbooks.io/decentralized-web-primer/content/files-on-ipfs/lessons/wrap-directories-around-content.html). Valid options are: true or false
* `groupId` - Used to upload a file to an existing [Group](/pinning/groups).
Here is an example of the object to create `pinataOptions`:
```json
"pinataOptions": {
"cidVersion": 0,
"wrapWithDirectory": false,
"groupId": "ID of the group you want to upload a CID to"
}
```
# Pinning Files
With Pinata, there are a few ways you can pin files to IPFS!
HTML uploads are currently only available on paid plans with granted access. If you are on a paid plan and wish to upload HTML please send a request through our support chat or send an email to [team@pinata.cloud](mailto:team@pinata.cloud)
## API & SDKs
If you’re a developer that needs to build decentralized applications, then you will likely want to use the [SDK](/ipfs-sdk) or the [Pinata API](/api-reference/introduction). These make it simple to integrate IPFS uploads into your App!
### Using the SDK
The IPFS SDK is able to handle mutliple kinds of uploads, whether its file objects, base64 strings, or even content from remote URLs.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
```
The SDK is also equipped to handle an array of file objects.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file1 = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const file2 = new File(["hello world again!"], "hello2.txt", { type: "text/plain" })
const upload = await pinata.upload.fileArray([file1, file2])
```
### 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! Consider using a [signed JWT approach](https://www.pinata.cloud/blog/how-to-upload-to-ipfs-from-the-frontend-with-signed-jwts).
The API accepts `File` objects (and blobs in the right environment) through a multipart form data request, where the object has a key of `file`. Additionally you can add [pinataMetadata](/pinning/pinata-metadata) and [pinataOptions](/pinning/pinata-metadata) to the request. Here is an example of how you could upload with the API:
```javascript
const JWT = "YOUR_PINATA_JWT";
async function pinFileToIPFS() {
try {
const formData = new FormData();
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
formData.append("file", file);
const pinataMetadata = JSON.stringify({
name: "File name",
});
formData.append("pinataMetadata", pinataMetadata);
const pinataOptions = JSON.stringify({
cidVersion: 1,
});
formData.append("pinataOptions", pinataOptions);
const request = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
method: "POST",
headers: {
Authorization: `Bearer ${JWT}`
},
body: formData,
});
const response = await request.json();
console.log(response);
} catch (error) {
console.log(error);
}
}
```
### Common File Recipes
Below are some common recipes for uploading a file.
#### Blob
Usually you can pass a Blob directly into the request but to help guarantee success we recommend passing it into a `File` object.
```typescript Blob
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const text = "Hello World!";
const blob = new Blob([text], { type: "text/plain" });
const file = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const upload = await pinata.upload.file(file)
```
#### JSON
Pinata makes it easy to upload JSON objects using the [json](/ipfs-sdk/upload/json) method.
```typescript JSON
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
```
#### Local Files
If you need to upload files from a local file source you can use `fs` to feed a file into a `blob`, then turn that `blob` into a `File`.
```typescript
const { PinataSDK } = require("pinata-web3")
const fs = require("fs")
const { Blob } = require("buffer")
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud"
})
const blob = new Blob([fs.readFileSync("./hello-world.txt")]);
const file = new File([blob], "hello-world.txt", { type: "text/plain"})
const upload = await pinata.upload.file(file);
```
#### URL
To upload a file from an external URL you can stream the contents into an `arrayBuffer`, which then gets passed into a new `Blob` that can then be uploaded to Pinata. This has been abstracted in the SDK using the [url](/ipfs-sdk/upload/url) method.
```typescript URL
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif")
```
#### base64
To upload a file in base64 simply turn the contents into a `buffer` that is passed into a `Blob`. Alternatively you can use the SDK for this as well using the [base64](/ipfs-sdk/upload/base64) method.
```typescript base64
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh")
```
#### Folders
The SDK can accept an array of files using the [fileArray](/ipfs-sdk/upload/file-array) method. 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.
```typescript SDK
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file1 = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const file2 = new File(["hello world again!"], "hello2.txt", { type: "text/plain" })
const upload = await pinata.upload.fileArray([file1, file2])
```
```javascript Node.js
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()
```
```javascript React
import { useState } from "react";
function App() {
const [selectedFile, setSelectedFile]: any = useState();
const changeHandler = (event: any) => {
setSelectedFile(event.target.files);
};
const handleSubmission = async () => {
try {
const formData = new FormData();
Array.from(selectedFile).forEach((file) => {
formData.append("file", file);
});
const metadata = JSON.stringify({
name: "File name",
});
formData.append("pinataMetadata", metadata);
const options = JSON.stringify({
cidVersion: 0,
});
formData.append("pinataOptions", options);
const res = await fetch(
"https://api.pinata.cloud/pinning/pinFileToIPFS",
{
method: "POST",
headers: {
Authorization: `Bearer ${import.meta.env.VITE_PINATA_JWT}`,
},
body: formData,
}
);
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
return (
<>
>
);
}
export default App;
```
```javascript Javascript
import FormData from "form-data"
const pinDirectoryToIPFS = async () => {
try {
const folder = "json";
const json1 = { hello: "world" };
const json2 = { hello: "world2" };
const blob1 = new Blob([JSON.stringify(json1, null, 2)], {
type: "application/json",
});
const blob2 = new Blob([JSON.stringify(json2, null, 2)], {
type: "application/json",
});
const files = [
new File([blob1], "hello.json", { type: "application/json" }),
new File([blob2], "hello2.json", { type: "application/json" }),
];
const data = new FormData();
Array.from(files).forEach((file) => {
// If you are not using `fs` you might need to specify the folder path along with the filename
data.append("file", file, `${folder}/${file.name}`);
});
const pinataMetadata = JSON.stringify({
name: `${folder}`,
});
data.append("pinataMetadata", pinataMetadata);
const res = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", {
method: "POST",
headers: {
Authorization: `Bearer ${PINATA_JWT}`,
},
body: data,
});
const resData = await res.json();
console.log(resData);
} catch (error) {
console.log(error);
}
};
pinDirectoryToIPFS();
```
We also have other tools like the [Pinata CLI](/sdks/pinata-cli) or [Next.js Starter](/sdks/nextjs-starter) which can be used to upload using [API Keys](/account-management/api-keys)!
## Web App
If you’re non-technical, you can use **[Pinata App](https://app.pinata.cloud/)** 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](https://app.pinata.cloud/pinmanager)**. 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](https://app.pinata.cloud/register)!
## 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 the SDK using the [cid](/ipfs-sdk/upload/cid) method.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const pin = await pinata.upload.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
This will result in a `request_id` and Pinata will start looking for the file. Progress can be checked by using the [pinJobs](/ipfs-sdk/data/pin-jobs) method.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const jobs = await pinata.pinJobs().status("prechecking")
```
All possible filters are included in the API reference below, but these are the possible "status" filters:
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.
```javascript
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()
```
## Peering with Pinata
If you run IPFS infrastructure and would like to peer with Pinata's nodes you can do so with the [Kubo](https://docs.ipfs.tech/reference/kubo/cli/) commands listed below. Rather than using full multiaddresses for our node IDs we use a DNS setup that is more stable and allows our infrastructure to be flexible.
```bash
ipfs swarm connect /dnsaddr/bitswap.pinata.cloud
```
```bash
ipfs config --json Peering.Peers '[{ "ID": "Qma8ddFEQWEU8ijWvdxXm3nxU7oHsRtCykAaVz8WUYhiKn", "Addrs": ["/dnsaddr/bitswap.pinata.cloud"] }]'
```
If you have any issues feel free to [reach out](mailto:team@pinata.cloud)!
# Signatures
Learn how to use Pinata to cryptographically sign CIDs
In a post-AI world it will become more and more evident that every piece of content will need a cryptographic signature to verify it's authenticity. Pinata is taking steps in this direction with the [Signatures API](/ipfs-sdk/signatures) and the [Content Addressable Gateway Plugin](/gateways/plugins/content-addressable).
## Signature Standard
Pinata is using the EIP-712 signature standard for signing CIDs with the following domain and types.
```typescript
export const domain = {
name: "Sign Content",
version: "1.0.0",
chainId: 1,
} as const;
export const types = {
Sign: [
{ name: "address", type: "address" },
{ name: "cid", type: "string" },
{ name: "date", type: "string" },
],
EIP712Domain: [
{
name: "name",
type: "string",
},
{
name: "version",
type: "string",
},
{
name: "chainId",
type: "uint256",
},
],
};
```
### address
* Type: `address`
The wallet address of the user singing the CID
```
0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826
```
### cid
* Type: `string`
The target CID to be signed
```
bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4
```
### date
* Type: `string`
The date the target CID was first uploaded to Pinata
```
2024-07-29T18:29:47.355Z
```
## Creating a Signature
In order to sign a CID you can use any library that support EIP-712 signing, like the example below with [viem](https://viem.sh/docs/actions/wallet/signTypedData).
```typescript example.ts
import { account, walletClient } from './config'
import { domain, types } from './data'
const signature = await walletClient.signTypedData({
account,
domain,
types,
primaryType: 'Sign',
message: {
address: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
cid: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4',
date: "2024-07-29T18:29:47.355Z"
}
})
```
```typescript data.ts
export const domain = {
name: "Sign Content",
version: "1.0.0",
chainId: 1,
} as const;
export const types = {
Sign: [
{ name: "address", type: "address" },
{ name: "cid", type: "string" },
{ name: "date", type: "string" },
],
EIP712Domain: [
{
name: "name",
type: "string",
},
{
name: "version",
type: "string",
},
{
name: "chainId",
type: "uint256",
},
],
};
```
```typescript config.ts
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'
export const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum!),
})
export const [account] = await walletClient.getAddresses()
↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
↑ Local Account
```
## Adding Signature to CID
In order to attach a signature to a CID the following requirements must be met:
* The CID being signed is owned by the signer
* The CID being signed was first uploaded by the signer
* The CID must not already have an existing signature with Pinata
After creating the signature with the previous step you can add it to the CID with the [add](/ipfs-sdk/signatures/add) method in the SDK.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.add({
cid: "QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU",
signature: "0x1b...911b"
});
```
## Getting a Signature for a CID
There are two ways you can an existing signature for a CID: the [get](/ipfs-sdk/signatures/get) method in the SDK or the [Content Addressable Gateway Plugin](/gateways/plugins/content-addressable).
### Content Addressable Gateway Plugin
After [installing the plugin](/gateways/plugins/getting-started#installing-plugins) you can simply request a CID through the Dedicated Gateway and get the signature in the header `pinata-signauture`.
```typescript
const signatureReq = await fetch(
`https://.mypinata.cloud/ipfs/`,
{
method: "HEAD",
}
);
const signature = signatureReq.headers.get("pinata-signature");
```
### SDK
You can also use the [get](/ipfs-sdk/signatures/get) method to get a signature for a given CID.
This method will check all CIDs on Pinata and will return a signature if it exists
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.get(
"QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU"
);
```
## Verifying a Signature
Since the signatures are using the EIP-712 standard you can use a library like [Viem](https://viem.sh/docs/utilities/verifyTypedData) to verify with the same typed data used to create it.
```typescript example.ts
import { account, walletClient } from './config'
import { domain, types } from './data'
import { verifyTypedData } from 'viem'
const CID = "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
const signatureReq = await fetch(
`https://example-gateway.mypinata.cloud/ipfs/${CID}`,
{
method: "HEAD",
}
);
const signature = signatureReq.headers.get("pinata-signature");
const valid = await verifyTypedData({
address: account.address,
domain,
types,
primaryType: 'Sign',
message: {
address: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
cid: 'bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4',
date: "2024-07-29T18:29:47.355Z"
},
signature,
})
```
```typescript data.ts
export const domain = {
name: "Sign Content",
version: "1.0.0",
chainId: 1,
} as const;
export const types = {
Sign: [
{ name: "address", type: "address" },
{ name: "cid", type: "string" },
{ name: "date", type: "string" },
],
EIP712Domain: [
{
name: "name",
type: "string",
},
{
name: "version",
type: "string",
},
{
name: "chainId",
type: "uint256",
},
],
};
```
```typescript config.ts
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'
export const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum!),
})
export const [account] = await walletClient.getAddresses()
↑ JSON-RPC Account
// export const account = privateKeyToAccount(...)
↑ Local Account
```
## Removing a Signature for a CID
To delete an existing signautre for a given CID you can use the [delete](/ipfs-sdk/signatures/delete) method.
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.delete(
"QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU"
);
```
# Config
Overview of the IPFS SDK Config
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
```
## Parameters
### pinataJwt
* Type: `string`
The Pinata API JWT key that authorizes the SDK. [Read more about API Keys](/account-management/api-keys).
### pinataGateway (Optional)
* Type: `string`
The domain of the Dedicated Gateway included with your account. [Read more about Dedicated Gateways](/gateways/dedicated-ipfs-gateways).
### pinataGatewayKey (Optional)
* Type: `string`
If you are using a [Gateway Access Control Key](/gateways/gateway-access-controls#gateway-keys) you can list it to be used automatically in any of the [gateway methods](/sdk-beta/gateways).
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
pinataGatewayKey: ""
});
```
# listFiles
List and filter files pinned to your Pinata account
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const files = await pinata.listFiles().name("pinnie")
```
## Returns
```typescript
type PinListItem = {
id: string;
ipfs_pin_hash: string;
size: number;
user_id: string;
date_pinned: string;
date_unpinned: string | null;
metadata: {
name: string | null;
keyvalues: {
[key: string]: any;
} | null;
};
regions: {
regionId: string;
currentReplicationCount: number;
desiredReplicationCount: number;
}[];
mime_type: string;
number_of_files: number;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Name of the file
```typescript
const files = await pinata
.listFiles()
.name('pinnie')
```
### cid
* Type: `string`
CID of the file
```typescript
const files = await pinata
.listFiles()
.cid('rbafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4r')
```
### group
* Type: `string`
Filter by `groupId`
```typescript
const files = await pinata
.listFiles()
.group('648767d5-0a80-4b3a-9669-93693e4a0519')
```
### keyValue
* Type: `object`
Filter files by `keyValue` objects used in Pinata Metadata
```typescript
const files = await pinata
.listFiles()
.keyValues("whimsey", 100)
```
This also includes an optional `operator` for even more fine tuned data
Type:
```typescript
operator?:
| "gt" // Greater than
| "gte" // Greater or equal to
| "lt" // Less than
| "lte" // Less than or equal to
| "ne" // Not equal
| "eq" // Equal
| "between"
| "notBetween"
| "like"
| "notLike"
| "iLike"
| "notILike"
| "regexp"
| "iRegexp";
```
```typescript
const files = await pinata
.listFiles()
.keyValues("whimsey", 400, "lt")
```
### pageLimit
* Type: `number`
Determine the number of results, default is `10`, max is `1000`
```typescript
const files = await pinata
.listFiles()
.pageLimit(100)
```
### pageOffset
* Type: `number`
Offset the number of files returned to paginate
```typescript
const files = await pinata
.listFiles()
.pageOffset(100)
```
### pinStart
* Type: `string`
Filter by start date in ISO8601 format
```typescript
const files = await pinata
.listFiles()
.pinStart('2024-07-16T11:41:19Z')
```
### pinEnd
* Type: `string`
Filter by end date in ISO8601 format
```typescript
const files = await pinata
.listFiles()
.pinEnd('2024-07-16T11:41:19Z')
```
### pinSizeMin
* Type: `number`
Filter by minimum file size in bytes
```typescript
const files = await pinata
.listFiles()
.pinSizeMin('100000')
```
### pinSizeMax
* Type: `number`
Filter by maximum file size in bytes
```typescript
const files = await pinata
.listFiles()
.pinSizeMax('1000000')
```
## Auto Paginate
The `listFiles` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.listFiles().name("pinnie") {
console.log(item.ipfs_pin_hash);
}
```
Works like magic ✨
# pinJobs
List pin by CID jobs
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const jobs = await pinata.pinJobs().status("prechecking")
```
## Returns
```typescript
type PinJobItem = {
id: string;
ipfs_pin_hash: string;
date_queued: string;
name: string;
status: string;
keyvalues: any;
host_nodes: string[];
pin_policy: {
regions: {
id: string;
desiredReplicationCount: number;
}[];
version: number;
};
};
```
## Parameters
Filter results with the following methods. All filters are optional.
### cid
* Type: `string`
Filter by `cid`
```typescript
const jobs = await pinata
.pinJobs()
.cid('bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4')
```
### status
* Type: ` "prechecking" | "retrieving" | "expired" | "over_free_limit" | "over_max_size" | "invalid_object" | "bad_host_node"`
Filter by current status
```typescript
const jobs = await pinata
.pinJobs()
.status("prechecking")
```
### sort
* Type: `"ASC" | "DSC"`
Order results by either ascending or descending by submission
```typescript
const jobs = await pinata
.pinJobs()
.sort("ASC")
```
### pageLimit
* Type: `number`
Limit the number of results. Default is `10`, max is `250`
```typescript
const jobs = await pinata
.pinJobs()
.pageLimit(100)
```
### pageOffset
* Type: `number`
Offset the number of files returned to paginate
```typescript
const jobs = await pinata
.pinJobs()
.pageOffset(100)
```
## Auto Paginate
The `pinJobs` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const job of pinata.pinJobs().status("expired") {
console.log(job.ipfs_pin_hash);
}
```
# testAuthenticaiton
Tests authentication with the current `PINATA_JWT`
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const auth = await pinata.testAuthentication()
```
## Returns
```typescript
type AuthTestResponse = {
message: string;
};
```
# unpin
Unpin an array of files from your account
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const unpin = await pinata.unpin([
"bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
])
```
## Returns
```typescript
type UnpinResponse[] = {
hash: string;
status: string;
};
```
## Parameters
### files
* Type: `string[]`
An array of CIDs you want to unpin
```typescript
const unpin = await pinata
.unpin([
"bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
"QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"
])
```
# updateMetadata
Update the metadata for an existing file
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const update = await pinata.updateMedatadata({
cid: "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
name: "Pinnie V2",
keyValues: {
whimsey: 200
}
})
```
## Returns
```typescript
OK
```
## Parameters
### cid
* Type: `string`
CID of the file you want to update
```typescript
const update = await pinata.updateMedatadata({
cid: "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
name: "Pinnie V2",
})
```
### name (Optional)
* Type: `string`
Update the name of a file that will appear in `listFiles`
```typescript
const update = await pinata.updateMedatadata({
cid: "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
name: "Pinnie V2",
})
```
### keyValues (Optional)
* Type: `object`
Update the `keyValues` object for a file
```typescript
const update = await pinata.updateMedatadata({
cid: "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
keyValues: {
whimsey: 200
}
})
```
# convert
Convert an IPFS link into one that uses your Dedicated Gateway
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const url = await pinata.gateways.convert(
"ipfs://QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"
);
```
## Returns
```typescript
string
```
## Parameters
### url
* Type: `string`
Accepts CID or IPFS gateway link.
```typescript
const url = await pinata.gateways.convert(
"bafybeibo5zcqeorhqxczodrx52rn7byyrwfvwthz5dspnjlbkd7zkugefi/hello-1.txt",
);
const url = await pinata.gateways.convert(
"https://ipfs.io/ipfs/bafybeibo5zcqeorhqxczodrx52rn7byyrwfvwthz5dspnjlbkd7zkugefi/hello-1.txt",
);
const url = await pinata.gateways.convert(
"https://bafyreibroegmxohcbvvs3rziqsp3osyn7t5rzot34y6pn5xtewffhtsl4e.ipfs.nftstorage.link/metadata.json",
);
```
### prefix (Optional)
* Type: `string`
Use a different gateway prefix than the config default
```typescript
const url = await pinata.gateways.convert(
"QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng",
"https://ipfs.io"
);
```
# dateIntervalAnalytics
Get specific usage analytics for a date range and interval
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.sort("desc");
```
## Returns
```typescript
export type TimeIntervalGatewayAnalyticsResponse = {
total_requests: number;
total_bandwidth: number;
time_periods: TimePeriodItem[];
};
export type TimePeriodItem = {
period_start_time: string;
requests: number;
bandwidth: number;
};
```
## Parameters
Filter response with the following additional methods.
### domain
* Type: `string`
Domain of the Gateway to get analytics for. Must be owned by the same account that is being authorized.
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud"
})
```
### start
* Type: `string`
Start date for the analytics query in the format `YYYY-MM-DD`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
start: "2024-08-01"
})
```
### end
* Type: `string`
End date for the analytics query in the format `YYYY-MM-DD`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
end: "2024-08-15"
})
```
### interval
* Type: `day` | `week`
Aggregate results by either `day` or `week` intervals
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
interval: "day"
})
```
### sortBy (Optional)
* Type: `requests` | `bandwidth`
Sort results by either `requests` or `bandwidth`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.sortBy("requests");
```
### cid (Optional)
* Type: `string`
Filter results by CID
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng");
```
### fileName (Optional)
* Type: `string`
Filter results by file name
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.fileName("pinnie.png");
```
### userAgent (Optional)
* Type: `string`
Filter results by a target user agent
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.userAgent("Go-http-client/2.0");
```
### country (Optional)
* Type: `string`
Filter results by country
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.country("us");
```
### region (Optional)
* Type: `string`
Filter results by a target region with the format `lowercaseCountry - upperCaseRegion`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.region("us - TN");
```
### referer (Optional)
* Type: `string`
Filter results by a target referer
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.referer("https://docs.pinata.cloud");
```
### limit (Optional)
* Type: `number`
Limit the number of results, default is `100`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.limit(100);
```
### sort (Optional)
* Type: `asc` | `desc`
Sort the result values by either `asc` or `desc`
```typescript
const analytics = await pinata.gateways.dateIntervalAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
interval: "day"
})
.sort("asc")
```
# deleteSwap
Remove a CID swap for [Hot Swaps](/gateways/plugins/how-swaps) plugin
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const deleteSwap = await pinata.gateways.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
```
## Returns
```typescript
OK
```
## Parameters
Pass in the required parameters below to remove a CID swap
### cid
* Type: `string`
This would be the original CID that was swapped
```typescript
const deleteSwap = await pinata.gateways.deleteSwap(
"bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske"
)
```
# get
Retrieve a file from IPFS through the config's `pinataGateway`
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = await pinata.gateways.get("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
## Returns
Returns the data from the CID in the form of `JSON`, `string`, or `Blob` as well as the `ContentType`
```typescript
type GetCIDResponse = {
data?: JSON | string | Blob | null;
contentType: ContentType;
};
type ContentType =
| "application/json"
| "application/xml"
| "text/plain"
| "text/html"
| "text/css"
| "text/javascript"
| "application/javascript"
| "image/jpeg"
| "image/png"
| "image/gif"
| "image/svg+xml"
| "audio/mpeg"
| "audio/ogg"
| "video/mp4"
| "application/pdf"
| "application/octet-stream"
| string;
```
## Parameters
### cid
* Type: `string`
Accepts CID or IPFS gateway link.
```typescript
const data = await pinata.gateways.get(
"bafybeibo5zcqeorhqxczodrx52rn7byyrwfvwthz5dspnjlbkd7zkugefi/hello-1.txt",
);
const data = await pinata.gateways.get(
"https://ipfs.io/ipfs/bafybeibo5zcqeorhqxczodrx52rn7byyrwfvwthz5dspnjlbkd7zkugefi/hello-1.txt",
);
const data = await pinata.gateways.get(
"https://bafyreibroegmxohcbvvs3rziqsp3osyn7t5rzot34y6pn5xtewffhtsl4e.ipfs.nftstorage.link/metadata.json",
);
```
## Methods
### optimizeImage (Optional)
* Type: [OptimizeImageOptions](/sdk/types#optimizeimageoptions)
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
If the content being fetched is an image you can apply image optimizations to the image.
```typescript
const data = await pinata.gateways
.get("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
.optimizeImage({
width: 500,
height: 500,
format: "webp"
})
```
# swapCid
Swap a CID for another using the [Hot Swaps](/gateways/plugins/hot-swaps) gateway plugin
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const swap = await pinata.gateways.swapCid({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
## Returns
```typescript
export type SwapCidResponse = {
mappedCid: string;
createdAt: string;
};
```
## Parameters
Pass in the required parameters below to swap a CID
### cid
* Type: `string`
This would be the original CID that would be visted
```typescript
const swap = await pinata.gateways.swapCid({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
})
```
### swapCid
* Type: `string`
This would be the CID you would want the old CID to point to
```typescript
const swap = await pinata.gateways.swapCid({
swapCid: "bafkreihumyr3bgxulu45ghws33xokwjm5o7xnkkgakaz66ldtylwiecnhu"
})
```
# swapHistory
See the history of [Hot Swaps](/gateways/plugins/hot-swaps) on a Gateway domain for a specified CID. This can be called by any Pinata user for any Gateway domain and CID.
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const history = await pinata.gateways.swapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
domain: "discordpinnie.mypinata.cloud"
})
```
## Returns
```typescript
SwapCidResponse[]
export type SwapCidResponse = {
mappedCid: string;
createdAt: string;
};
```
## Parameters
Pass in the required parameters to get a swap history
### cid
* Type: `string`
The target CID for swap history
```typescript
const history = await pinata.gateways.swapHistory({
cid: "bafkreibbvdqf5ekc2crouowv7vtjbcmbjoiysegslmmqc6jrxbaa43xske",
})
```
### domain
* Type: `string`
The Gateway domain that has the Hot Swaps plugin installed
```typescript
const history = await pinata.gateways.swapHistory({
domain: "discordpinnie.mypinata.cloud"
})
```
# topUsageAnalytics
Get top ranking analytic data for a Gateway
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "cid"
})
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng");
```
## Returns
```typescript
export type TopGatewayAnalyticsItem = {
value: string;
requests: number;
bandwidth: number;
};
```
## Parameters
Filter response with the following additional methods.
### domain
* Type: `string`
Domain of the Gateway to get analytics for. Must be owned by the same account that is being authorized.
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud"
})
```
### start
* Type: `string`
Start date for the analytics query in the format `YYYY-MM-DD`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
start: "2024-08-01"
})
```
### end
* Type: `string`
End date for the analytics query in the format `YYYY-MM-DD`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
end: "2024-08-15"
})
```
### sortBy
* Type: `requests` | `bandwidth`
Sort results by either `requests` or `bandwidth`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
sortBy: "requests"
})
```
### attribute
* Type: `cid` | `country` | `region` | `user_agent` | `referer` | `file_name`
Target attribute to aggregate results with
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
attribute: "cid"
})
```
### cid (Optional)
* Type: `string`
Filter results by CID
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "cid"
})
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng");
```
### fileName (Optional)
* Type: `string`
Filter results by file name
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "file_name"
})
.fileName("pinnie.png");
```
### userAgent (Optional)
* Type: `string`
Filter results by a target user agent
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "user_agent"
})
.userAgent("Go-http-client/2.0");
```
### country (Optional)
* Type: `string`
Filter results by country
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "country"
})
.country("us");
```
### region (Optional)
* Type: `string`
Filter results by a target region with the format `lowercaseCountry - upperCaseRegion`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "country"
})
.region("us - TN");
```
### referer (Optional)
* Type: `string`
Filter results by a target referer
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "referer"
})
.referer("https://docs.pinata.cloud");
```
### limit (Optional)
* Type: `number`
Limit the number of results, default is `100`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "referer"
})
.limit(100);
```
### sort (Optional)
* Type: `asc` | `desc`
Sort the result values by either `asc` or `desc`
```typescript
const analytics = await pinata.gateways.topUsageAnalytics({
domain: "docs.mypinata.cloud",
start: "2024-08-01",
end: "2024-08-15",
sortBy: "requests",
attribute: "referer"
})
.sort("asc")
```
# Getting Started
Get up and running with the IPFS SDK
The Pinata Typescript SDK is an all-in-one tool for everything you might need, from uploading content, using Dedicated Gateways, even user or group management!
## 1. Installation and Setup
Install with your package manager of choice
```bash npm
npm i pinata-web3
```
```bash pnpm
pnpm i pinata-web3
```
```bash yarn
yarn add pinata-web3
```
```bash bun
bun i pinata-web3
```
Import and initialize the SDK in your codebase with the following variables
* [Pinata API Key JWT](/web3/account-management/api-keys)
* [Pinata Dedicated Gateway Domain](/web3/gateways/dedicated-ipfs-gateways)
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: "PINATA_JWT",
pinataGateway: "example-gateway.mypinata.cloud",
});
```
The `PINATA_JWT` is a secret key, be sure to initialize the SDK in a secure environment and practice basic variable security practices. If you need to upload from a client environment, consider using signed JWTs
## 2. Upload a File
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
async function main() {
try {
const file = new File(["hello"], "Testing.txt", { type: "text/plain" });
const upload = await pinata.upload.file(file);
console.log(upload);
} catch (error) {
console.log(error);
}
}
await main();
```
This will return an object like the following
```typescript
{
IpfsHash: "bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq",
PinSize: 5,
Timestamp: "2024-07-11T23:33:27.665Z",
}
```
## 3. Retrieve a File
Use the CID or `IpfsHash` from the upload to fetch a file
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
async function main() {
try {
const data = await pinata.gateways.get("bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq");
console.log(data)
} catch (error) {
console.log(error);
}
}
await main();
```
# addCids
Add CIDs to a Group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.addCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
cids: ["QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"],
});
```
## Returns
```typescript
OK
```
## Parameters
### groupId
* Type: `string`
ID of the target Group to add files to
```typescript
const group = await pinata.groups.addCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
### cids
* Type: `string[]`
An array of CIDs as strings that you want to add to the group
```typescript
const group = await pinata.groups.addCids({
cids: [
"QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng",
"bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
],
});
```
# create
Create a Group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.create({
name: "My New Group",
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
user_id: string;
name: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
### name
* Type: `string`
Requires a name for the group to be created
```typescript
const group = await pinata.groups.create({
name: "My New Group",
});
```
# delete
Delete a Group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
## Returns
```typescript
OK
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript
const groups = await pinata.groups.delete({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
# get
Get info for an existing group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
user_id: string;
name: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript
const groups = await pinata.groups.get({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
# list
List and filter through all Groups
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups
.list()
.name("Greetings");
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
user_id: string;
name: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Filter by name, uses "contains" matching
```typescript
const groups = await pinata.groups
.list()
.name("Greetings");
```
### limit
* Type: `number`
Limit the number of results
```typescript
const groups = await pinata.groups
.list()
.limit(5);
```
### offset
* Type: `number`
Offset the number of groups returned to paginate
```typescript
const groups = await pinata.groups
.list()
.offset(10);
```
## Auto Paginate
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.gateways.list()) {
console.log(item.name);
}
```
# removeCids
Remove CIDs from a Group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const group = await pinata.groups.removeCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
cids: ["QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng"],
});
```
## Returns
```typescript
OK
```
## Parameters
### groupId
* Type: `string`
ID of the target Group to remove files from
```typescript
const group = await pinata.groups.removeCids({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
### cids
* Type: `string[]`
An array of CIDs as strings that you want to remove from the group
```typescript
const group = await pinata.groups.removeCids({
cids: [
"QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng",
"bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
],
});
```
# update
Update the name of a group
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
name: "My New Group 2"
});
```
## Returns
```typescript
type GroupResponseItem = {
id: string;
user_id: string;
name: string;
updatedAt: string;
createdAt: string;
};
```
## Parameters
### groupId
* Type: `string`
ID for the target Group
```typescript
const groups = await pinata.groups.update({
groupId: "3778c10d-452e-4def-8299-ee6bc548bdb0",
});
```
### name
* Type: `string`
Updated name for the target group
```typescript
const groups = await pinata.groups.update({
name: "My Group Again",
});
```
# create
Create an API key
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
},
maxUses: 1,
});
```
## Returns
```typescript
type KeyResponse = {
JWT: string;
pinata_api_key: string;
pinata_api_secret: string;
};
```
## Parameters
### keyName
* Type: `string`
Name for the API key
```typescript
const key = await pinata.keys.create({
keyName: "user 1"
});
```
### maxUses (Optional)
* Type: `number`
Limit the number of uses a key is valid for
```typescript
const key = await pinata.keys.create({
maxUses: 2
});
```
### permissions
* Type: [KeyPermissions](../types#keypermissions)
#### admin (Optional)
* Type: `boolean`
Grants the key admin access to all endpoints
```typescript
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
admin: true,
}
});
```
#### endpoints (Optional)
* Type [Endpoints](../types#endpoints)
```typescript
const key = await pinata.keys.create({
keyName: "user 1",
permissions: {
endpoints: {
data: {
pinList: true,
userPinnedDataTotal: false
},
pinning: {
hashMetadata: true,
hashPinPolicy: false,
pinByHash: true,
pinFileToIPFS: true,
pinJSONToIPFS: true,
pinJobs: false,
unpin: false,
userPinPolicy: false
}
}
}
});
```
# list
List and filter through Keys
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const keys = await pinata.keys
.list()
.name("Admin")
.revoked(false)
```
## Returns
```typescript
type KeyListItem = {
id: string;
name: string;
key: string;
secret: string;
max_uses: number;
uses: number;
user_id: string;
scopes: KeyScopes;
revoked: boolean;
createdAt: string;
updatedAt: string;
};
```
## Parameters
Filter response with the following additional methods. All filters are optional.
### name
* Type: `string`
Filter by name, uses "contains" matching
```typescript
const keys = await pinata.keys
.list()
.name("Greetings");
```
### revoked
* Type: `boolean`
Filter keys by whether or not they have been revoked
```typescript
const keys = await pinata.keys
.list()
.revoked(false);
```
### exhausted
* Type: `boolean`
Filter keys based on whether they had limited uses that were exhuasted
```typescript
const keys = await pinata.keys
.list()
.exhausted(false);
```
### offset
* Type: `number`
Offset the number of keys returned to paginate
```typescript
const keys = await pinata.keys
.list()
.offset(5);
```
## Auto Paginate
The `list` method has an auto pagination feature that is triggered when used inside a `for await` iterator
```typescript
for await (const item of pinata.keys.list()) {
console.log(item.name);
}
```
# revoke
Revoke an API Key
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const revoke = await pinata.keys.revoke([
"94566af5e63833e260be"
]);
```
## Returns
```typescript
type RevokeKeyResponse[] = {
key: string;
status: string;
};
```
## Parameters
### keys
* Type: `string[]`
An array of API Keys to revoke. This is the `key` found in the response of `keys.list`
```typescript
const revoke = await pinata.keys.revoke([
"94566af5e63833e260be"
]);
```
# add
Add an EIP-712 signature to a CID
For more information about adding signatures to CIDs on IPFS please read the [Signatures Guide](/pinning/signatures).
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.add({
cid: "QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU",
signature: "0x1b...911b"
});
```
## Returns
```typescript
type SignatureResponse = {
cid: string;
signature: string;
};
```
## Parameters
### cid
* Type: `string`
Target CID that you want to add a signature to.
```typescript
const signature = await pinata.signatures.add({
cid: "QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU",
});
```
### signature
* Type: `0x${string}`
EIP-712 Signature to be assigned to target CID.
```typescript
const signature = await pinata.signatures.add({
signature: "0x1b...911b"
});
```
# delete
Delete an EIP-712 signature from a CID
For more information about adding signatures to CIDs on IPFS please read the [Signatures Guide](/pinning/signatures).
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.delete(
"QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU"
);
```
## Returns
```typescript
OK
```
## Parameters
### cid
* Type: `string`
Target CID that you want to add a signature to.
```typescript
const signature = await pinata.signatures.delete(
"QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU"
);
```
# get
Get signature for CID
For more information about adding signatures to CIDs on IPFS please read the [Signatures Guide](/pinning/signatures).
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const signature = await pinata.signatures.get("QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU");
```
## Returns
```typescript
type SignatureResponse = {
cid: string;
signature: string;
};
```
## Parameters
### cid
* Type: `string`
Target CID that you want to add a signature to.
```typescript
const signature = await pinata.signatures.get("QmXGeVy9dVwfuFJmvbzz8y4dYK1TdxXbDGzwbNuyZ5xXSU");
```
# null
## `AuthTestResponse`
```typescript
type AuthTestResponse = {
message: string;
};
```
## `ContainsCIDResponse`
```typescript
type ContainsCIDResponse = {
containsCid: boolean;
cid: string | null;
};
```
## `ContentType`
```typescript
type ContentType =
| "application/json"
| "application/xml"
| "text/plain"
| "text/html"
| "text/css"
| "text/javascript"
| "application/javascript"
| "image/jpeg"
| "image/png"
| "image/gif"
| "image/svg+xml"
| "audio/mpeg"
| "audio/ogg"
| "video/mp4"
| "application/pdf"
| "application/octet-stream"
| string
| null;
```
## `DataEndponts`
```typescript
type DataEndponts = {
pinList?: boolean;
userPinnedDataTotal?: boolean;
};
```
## `Endpoints`
```typescript
type Endpoints = {
data?: DataEndponts;
pinning?: PinningEndpoints;
};
```
## `FileObject`
```typescript
type FileObject = {
name: string;
size: number;
type: string;
lastModified: number;
arrayBuffer: () => Promise;
};
```
## `GatewayAnalyticsQuery`
```typescript
type GatewayAnalyticsQuery = {
gateway_domain: string;
start_date: string;
end_date: string;
cid?: string;
file_name?: string;
user_agent?: string;
country?: string;
region?: string;
referer?: string;
limit?: number;
sort_order?: "asc" | "desc";
};
```
## `GetCIDResponse`
```typescript
type GetCIDResponse = {
data?: JSON | string | Blob | null;
contentType: ContentType;
};
```
## `GetGroupOptions`
```typescript
type GetGroupOptions = {
groupId: string;
};
```
## `GroupCIDOptions`
```typescript
type GroupCIDOptions = {
groupId: string;
cids: string[];
};
```
## `GroupOptions`
```typescript
type GroupOptions = {
name: string;
};
```
## `GroupQueryOptions`
```typescript
type GroupQueryOptions = {
nameContains?: string;
offset?: number;
limit?: number;
};
```
## `GroupResponseItem`
```typescript
type GroupResponseItem = {
id: string;
user_id: string;
name: string;
updatedAt: string;
createdAt: string;
};
```
## `JsonBody`
```typescript
type JsonBody = Record;
```
## `KeyListItem`
```typescript
type KeyListItem = {
id: string;
name: string;
key: string;
secret: string;
max_uses: number;
uses: number;
user_id: string;
scopes: KeyScopes;
revoked: boolean;
createdAt: string;
updatedAt: string;
};
```
## `KeyListQuery`
```typescript
type KeyListQuery = {
revoked?: boolean;
limitedUse?: boolean;
exhausted?: boolean;
name?: string;
offset?: number;
};
```
## `KeyListResponse`
```typescript
type KeyListResponse = {
keys: KeyListItem[];
count: number;
};
```
## `KeyOptions`
```typescript
type KeyOptions = {
keyName: string;
permissions: KeyPermissions;
maxUses?: number;
};
```
## `KeyPermissions`
```typescript
type KeyPermissions = {
admin?: boolean;
endpoints?: Endpoints;
};
```
## `KeyResponse`
```typescript
type KeyResponse = {
JWT: string;
pinata_api_key: string;
pinata_api_secret: string;
};
```
## `KeyScopes`
```typescript
type KeyScopes = {
endpoints: {
pinning: {
pinFileToIPFS: boolean;
pinJSONToIPFS: boolean;
};
};
admin: boolean;
};
```
## `OptimizeImageOptions`
```typescript
type OptimizeImageOptions = {
width?: number;
height?: number;
dpr?: number;
fit?: "scaleDown" | "contain" | "cover" | "crop" | "pad";
gravity?: "auto" | "side" | string;
quality?: number;
format?: "auto" | "webp";
animation?: boolean;
sharpen?: number;
onError?: boolean;
metadata?: "keep" | "copyright" | "none";
};
```
## `PinByCIDResponse`
```typescript
type PinByCIDResponse = {
id: string;
ipfsHash: string;
status: "prechecking" | "retrieving";
name: string;
updated?: boolean;
};
```
## `PinataConfig`
```typescript
type PinataConfig = {
pinataJwt: string | undefined;
pinataGateway?: string;
pinataGatewayKey?: string;
customHeaders?: Record;
endpointUrl?: string;
};
```
## `PinataMetadata`
```typescript
type PinataMetadata = {
name?: string;
keyValues?: Record;
};
```
## `PinataMetadataUpdate`
```typescript
type PinataMetadataUpdate = {
cid: string;
name?: string;
keyValues?: Record;
};
```
## `PinJobItem`
```typescript
type PinJobItem = {
id: string;
ipfs_pin_hash: string;
date_queued: string;
name: string;
status: string;
keyvalues: any;
host_nodes: string[];
pin_policy: {
regions: {
id: string;
desiredReplicationCount: number;
}[];
version: number;
};
};
```
## `PinJobQuery`
```typescript
type PinJobQuery = {
sort?: "ASC" | "DSC";
status?:
| "prechecking"
| "retrieving"
| "expired"
| "over_free_limit"
| "over_max_size"
| "invalid_object"
| "bad_host_node";
ipfs_pin_hash?: string;
limit?: number;
offset?: number;
};
```
## `PinJobResponse`
```typescript
type PinJobResponse = {
rows: PinJobItem[];
};
```
## `PinListItem`
```typescript
type PinListItem = {
id: string;
ipfs_pin_hash: string;
size: number;
user_id: string;
date_pinned: string;
date_unpinned: string | null;
metadata: {
name: string | null;
keyvalues: {
[key: string]: any;
} | null;
};
regions: {
regionId: string;
currentReplicationCount: number;
desiredReplicationCount: number;
}[];
mime_type: string;
number_of_files: number;
};
```
## `PinListQuery`
```typescript
type PinListQuery = {
cid?: string;
pinStart?: string;
pinEnd?: string;
pinSizeMin?: number;
pinSizeMax?: number;
pageLimit?: number;
pageOffset?: number;
name?: string;
groupId?: string;
key?: string;
value?: string | number;
operator?:
| "gt"
| "gte"
| "lt"
| "lte"
| "ne"
| "eq"
| "between"
| "notBetween"
| "like"
| "notLike"
| "iLike"
| "notILike"
| "regexp"
| "iRegexp";
};
```
## `PinListResponse`
```typescript
type PinListResponse = {
rows: PinListItem[];
};
```
## `PinningEndpoints`
```typescript
type PinningEndpoints = {
hashMetadata?: boolean;
hashPinPolicy?: boolean;
pinByHash?: boolean;
pinFileToIPFS?: boolean;
pinJSONToIPFS?: boolean;
pinJobs?: boolean;
unpin?: boolean;
userPinPolicy?: boolean;
};
```
## `PinResponse`
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## `RevokeKeyResponse`
```typescript
type RevokeKeyResponse = {
key: string;
status: string;
};
```
## `SignatureOptions`
```typescript
type SignatureOptions = {
cid: string;
signature: string;
};
```
## `SignatureResponse`
```typescript
type SignatureResponse = {
cid: string;
signature: string;
};
```
## `SwapCidOptions`
```typescript
type SwapCidOptions = {
cid: string;
swapCid: string;
};
```
## `SwapCidResponse`
```typescript
type SwapCidResponse = {
mappedCid: string;
createdAt: string;
};
```
## `SwapHistoryOptions`
```typescript
type SwapHistoryOptions = {
cid: string;
domain: string;
};
```
## `TimeIntervalGatewayAnalyticsQuery`
```typescript
type TimeIntervalGatewayAnalyticsQuery = GatewayAnalyticsQuery & {
sort_by?: "requests" | "bandwidth";
date_interval: "day" | "week";
};
```
## `TimeIntervalGatewayAnalyticsResponse`
```typescript
type TimeIntervalGatewayAnalyticsResponse = {
total_requests: number;
total_bandwidth: number;
time_periods: TimePeriodItem[];
};
```
## `TimePeriodItem`
```typescript
type TimePeriodItem = {
period_start_time: string;
requests: number;
bandwidth: number;
};
```
## `TopGatewayAnalyticsItem`
```typescript
type TopGatewayAnalyticsItem = {
value: string;
requests: number;
bandwidth: number;
};
```
## `TopGatewayAnalyticsQuery`
```typescript
type TopGatewayAnalyticsQuery = GatewayAnalyticsQuery & {
sort_by: "requests" | "bandwidth";
attribute:
| "cid"
| "country"
| "region"
| "user_agent"
| "referer"
| "file_name";
};
```
## `UnpinResponse`
```typescript
type UnpinResponse = {
hash: string;
status: string;
};
```
## `UpdateGroupOptions`
```typescript
type UpdateGroupOptions = {
name: string;
groupId: string;
};
```
## `UploadCIDOptions`
```typescript
type UploadCIDOptions = {
metadata?: PinataMetadata;
peerAddresses?: string[];
keys?: string;
groupId?: string;
};
```
## `UploadOptions`
```typescript
type UploadOptions = {
metadata?: PinataMetadata;
pinType?: "async" | "sync" | "cidOnly";
keys?: string;
groupId?: string;
cidVersion?: 0 | 1;
};
```
## `UserPinnedDataResponse`
```typescript
type UserPinnedDataResponse = {
pin_count: number;
pin_size_total: number;
pin_size_with_replications_total: number;
};
```
# base64
Upload a base64 string to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh")
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### base64
* Type: `string`
Accepts a string encoded in base64
```typescript
const upload = await pinata.upload.base64("SGVsbG8gV29ybGQh")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.addMetadata({
name: "hello.txt",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.base64("SGVsbG8gV29ybGQh")
.cidVersion(0)
```
# cid
Pin an existing CID on IPFS to your Pinata account
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const pin = await pinata.upload.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
## Returns
```typescript
type PinByCIDResponse = {
id: string;
ipfsHash: string;
status: "prechecking" | "retrieving";
name: string;
updated?: boolean;
};
```
## Parameters
### cid
* Type: `string`
Target CID already on IPFS
```typescript
const pin = await pinata.upload.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const pin = await pinata.upload
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
.addMetadata({
name: "hello.txt",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const pin = await pinata.upload
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const pin = await pinata.upload
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
.key("GENERATED_API_JWT")
```
### peerAddress (Optional)
* Type: `string[]`
Accepts an array of host node IDs to peer with
```typescript
const pin = await pinata.upload
.cid("QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng")
.peerAddress([
"/ip4/hostNode1ExternalIP/tcp/4001/ipfs/hostNode1PeerId",
"/ip4/hostNode2ExternalIP/tcp/4001/ipfs/hostNode2PeerId"
])
```
# file
Upload a single file to Pinata
HTML uploads are currently only available on paid plans with granted access. If you are on a paid plan and wish to upload HTML please send a request through our support chat or send an email to [team@pinata.cloud](mailto:team@pinata.cloud)
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const upload = await pinata.upload.file(file)
```
### Local Files
If you need to upload files from a local file source you can use `fs` to feed a file into a `blob`, then turn that `blob` into a `File`.
```typescript
const { PinataSDK } = require("pinata-web3")
const fs = require("fs")
const { Blob } = require("buffer")
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud"
})
const blob = new Blob([fs.readFileSync("./hello-world.txt")]);
const file = new File([blob], "hello-world.txt", { type: "text/plain"})
const upload = await pinata.upload.file(file);
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### file
* Type: `File` object
Accepts a File object in accordance with the [W3C File API](https://w3c.github.io/FileAPI/#file-section).
```typescript
const blob = new Blob(["hello world!"], { type: "text/plain" })
const file = new File([blob], "hello.txt", { type: "text/plain" })
const upload = await pinata.upload.file(file)
```
In most environments you can also pass a Blob here as well.
```typescript
const blob = new Blob(["hello world!"], { type: "text/plain" })
const upload = await pinata.upload.file(blob)
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.file(file)
.addMetadata({
name: "pinnie.png",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.file(file)
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.file(file)
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.file(file)
.cidVersion(0)
```
# fileArray
Upload an array of files to Pinata as a folder
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const file1 = new File(["hello world!"], "hello.txt", { type: "text/plain" })
const file2 = new File(["hello world again!"], "hello2.txt", { type: "text/plain" })
const upload = await pinata.upload.fileArray([file1, file2])
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### file
* Type: `File[]` object
Accepts an array of File objects in accordance with the [W3C File API](https://w3c.github.io/FileAPI/#file-section).
```typescript
const file1 = new File([blob1], "hello1.txt", { type: "text/plain" })
const file2 = new File([blob2], "hello2.txt", { type: "text/plain" })
const upload = await pinata.upload.fileArray([file1, file2])
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.fileArray([file1, file2])
.addMetadata({
name: "pinnie.png",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.fileArray([file1, file2])
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.fileArray([file1, file2])
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.fileArray([file1, file2])
.cidVersion(0)
```
# json
Upload a JSON object to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### JSON
* Type: `Record`
Accepts an object that is turned into JSON
```typescript
const upload = await pinata.upload.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
attributes: [
{
name: "whimsey",
value: 100
}
]
})
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
.addMetadata({
name: "pinnie.png",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.json({
name: "Pinnie NFT",
description: "A Pinnie NFT from Pinata",
image: "ipfs://bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4"
})
.cidVersion(0)
```
# stream
Upload a single file stream to Pinata
HTML uploads are currently only available on paid plans with granted access. If you are on a paid plan and wish to upload HTML please send a request through our support chat or send an email to [team@pinata.cloud](mailto:team@pinata.cloud)
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
import fs from "fs"
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const stream = fs.createReadStream("path/to/file")
const upload = await pinata.upload.stream(stream)
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### stream
* Type: `NodeJS.ReadableStream`
Accepts a local file as a [ReadableStream](https://nodejs.org/api/webstreams.html#class-readablestream)
```typescript
const stream = fs.createReadStream("path/to/file")
const upload = await pinata.upload.stream(stream)
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.stream(stream)
.addMetadata({
name: "pinnie.png",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.stream(stream)
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.stream(stream)
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.stream(stream)
.cidVersion(0)
```
# url
Upload the contents of a URL to Pinata
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif")
```
## Returns
```typescript
type PinResponse = {
IpfsHash: string;
PinSize: number;
Timestamp: string;
isDuplicate?: boolean;
};
```
## Parameters
### base64
* Type: `string`
Accepts a URL in the form of a string. The mimetype of the URL body provided in the headers typically determines the resulting file.
```typescript
const upload = await pinata.upload.url("https://i.imgur.com/u4mGk5b.gif")
```
### addMetadata (Optional)
* Type: [PinataMetadata](../types#pinatametadata)
Add optional metadata to file
```typescript
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.addMetadata({
name: "hello.txt",
keyValues: {
whimsey: 100
}
})
```
### group (Optional)
* Type: `string`
Upload to a specific group by passing in the `groupId`
```typescript
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.group("b07da1ff-efa4-49af-bdea-9d95d8881103")
```
### key (Optional)
* Type: `string`
Upload a file using a secondary API key generated through `keys.create()`
```typescript
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.key("GENERATED_API_JWT")
```
### cidVersion (Optional)
* Type: `0 | 1`
Specificy CID version for upload
```typescript
const upload = await pinata.upload
.url("https://i.imgur.com/u4mGk5b.gif")
.cidVersion(0)
```
# pinnedFileCount
List total number of files currently pinned
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const pinnedFiles = await pinata.usage.pinnedFileCount();
```
## Returns
* Type: `number`
Number of total files pinned
# totalStorageSize
List total storage in bytes
## Usage
```typescript
import { PinataSDK } from "pinata-web3";
const pinata = new PinataSDK({
pinataJwt: process.env.PINATA_JWT!,
pinataGateway: "example-gateway.mypinata.cloud",
});
const pinnedFiles = await pinata.usage.totalStorageSize();
```
## Returns
* Type: `number`
Total storage used in bytes
# Community SDKs
## IPFS Gateway Tools SDK
This is a convenience SDK that allows developers to take an IPFS URL of any kind and convert it to a Gateway URL of their choosing. This is especially useful when you want to load IPFS content through your own [Dedicated Gateway](/gateways/dedicated-ipfs-gateways).
[https://www.npmjs.com/package/@pinata/ipfs-gateway-tool](https://www.npmjs.com/package/@pinata/ipfs-gateway-tool)
## Community SDKs
These SDKs have been written by members of the Pinata community. They have not been audited by anyone at Pinata, nor can we vouch for their security or viability.
### Python
[https://github.com/Vourhey/pinatapy](https://github.com/Vourhey/pinatapy)
[https://github.com/edmondyu/pinata-python-util](https://github.com/edmondyu/pinata-python-util)
### Go
[https://gitlab.com/benoit74/pinata-cli](https://gitlab.com/benoit74/pinata-cli)
### Rust
[https://github.com/perfectmak/pinata-sdk](https://github.com/perfectmak/pinata-sdk)
### .NET
[https://www.nuget.org/packages/Pinata.Client/](https://www.nuget.org/packages/Pinata.Client/)
### Elixir
[https://github.com/m1ome/ex\_pinata](https://github.com/m1ome/ex_pinata)
# Next.js Starter
If you're planning on using Pinata inside of a web application, this template is a great way to get started. Pinata uses the builtin Next.js `api` routes to keep your API keys secure, and we make it easy to spin up!
## Getting Started
### API Keys and Gateway
If you haven't already, visit the [keys page](https://app.pinata.cloud/developers/api-keys) and create an API key.
You can also use your own Dedicated Gateway domain, which you can get from the [Gateways page](https://app.pinata.cloud/gateway).
### Starting Up the App
Create a new Pinata project using this command:
```
npx create-pinata-app-web3
```
Follow the prompts in the command line to create the project. Once complete, change into the new project directory and run the app with the following command:
```
npm run dev
```
You can edit the `pages/index.js` file and the API route file `pages/api/files` to see the Pinata functionality and extend it or make changes.
### Environment Variables
This project makes use of both public and private environment variables. The private environment variables are used to protect sensitive data like your Pinata API keys. The public environment variables are convenient central variable housing.
Read more about [how environment variables work with Next.js here](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables).
There is a `.env.sample` file you can copy and rename to `.env.local` for use in your project. Be sure to fill out the environment variable values in the `.env.local` file with your actual values.
### Learn More
For more information about building apps with Pinata and IPFS, check out the following resources:
* [Pinata Docs](https://docs.pinata.cloud)
* [Pinata Tutorials](https://medium.com/pinata)
* [Quick Start Recipes](https://docs.pinata.cloud/recipes)
# Pinata CLI
The Pinata CLI written in Go is designed to help automate Pinata related tasks in the terminal. Its still in development so please feel free to open issues and pull requests on the [GitHub repo](https://github.com/PinataCloud/pinata-go-cli)!
## Installation
If you are on Windows please use WSL when installing, as the current implementation will not work natively on Windows OS.
We are currently working on the build flow for binaries to make installation easier, but for now we recommend building from source.
To do this make sure you have [Go](https://go.dev/) installed on your computer and the following command returns a version:
```bash
go version
```
Then paste and run the following into your terminal:
```bash
git clone https://github.com/PinataCloud/pinata-go-cli && cd pinata-go-cli && go install .
```
## Usage
The Pinata CLI is equipped with the majortiry of features on the Pinata API.
### `auth` - Authentication
With the CLI installed you will first need to authenticate it with your [Pinata JWT](/account-management/api-keys)
```bash
pinata auth
```
### `upload` - Uploads
After authentication you can now upload using the `upload` command or `u` for short, then pass in the path to the file or folder you want to upload.
```bash
pinata upload ~/Pictures/somefolder/image.png
```
The following flags are also available to set the name or CID version of the upload.
```bash
--version value, -v value Set desired CID version to either 0 or 1. Default is 1. (default: 1)
--name value, -n value Add a name for the file you are uploading. By default it will use the filename on your system. (default: "nil")
--cid-only Use if you only want the CID returned after an upload (default: false)
```
### `list` - List Files
You can list files with the `list` command or the alias `l`. The results are printed in raw JSON to help increase composability.
```bash
pinata list
```
By default it will retrieve the 10 latest files, but with the flags below you can get more results or fine tune your search.
```bash
--cid value, -c value Search files by CID (default: "null")
--amount value, -a value The number of files you would like to return, default 10 max 1000 (default: "10")
--name value, -n value The name of the file (default: "null")
--status value, -s value Status of the file. Options are 'pinned', 'unpinned', or 'all'. Default: 'pinned' (default: "pinned")
--pageOffset value, -p value Allows you to paginate through files. If your file amount is 10, then you could set the pageOffset to '10' to see the next 10 files. (default: "null")
```
### `delete` - Delete Files
If you ever need to you can delete a file by CID using the `delete` command or alias `d` followed by the file CID.
```bash
pinata delete QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng
```
### `pin` - Pin by CID
Separate from the `upload` command which uploads files from your machine to Pinata, you can also pin a file already on the IPFS network by using the `pin` command or alias `p` followed by the CID. This will start a pin by CID request which will go into a queue.
```bash
pinata pin QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng
```
To check the queue use the `request` command.
### `requests` - Pin by CID Requests
As mentioned in the `pin` command, when you submit an existing CID on IPFS to be pinned to your Pinata account, it goes into a request queue. From here it will go through multiple status'. For more info on these please consult the [documentation](/api-reference/endpoint/list-pin-by-cid-jobs).
```bash
pinata requests
```
You can use flags to help filter requests as well.
```bash
--cid value, -c value Search pin by CID requests by CID (default: "null")
--status value, -s value Search by status for pin by CID requests. See https://docs.pinata.cloud/reference/get_pinning-pinjobs for more info. (default: "null")
--pageOffset value, -p value Allows you to paginate through requests by number of requests. (default: "null")
```
## Contact
If you have any questions please feel free to reach out to us!
[team@pinata.cloud](mailto:team@pinata.cloud)
# Pinata Raycast Extension
If you happen to use [Raycast](https://raycast.com), a MacOS tool for boosting your productivity, Pinata has an extension you can download from the Extension Store!
## Getting Started
In order to use this extension you will need a Pinata API Key JWT and a Dedicated Gateway, which are both included on the free plan! Here is a guide on making your API key, and here is where you can find your Dedicated Gateway domain.
You will also need to install Raycast to your computer which you can do by visit [Raycast](https://raycast.com).
Next visit the [Pinata Extension](https://raycast.com/pinata/pinata) store page and click "Install Extension" in the top right.
Once installed you can try running it, and it should prompt you to add some details before using the extension. Paste in your Pinata API Key JWT from earlier, as well as the domain for your Dedicated Gateway. It should be in the format of `https://mygateway.mypinata.cloud`.
## Uploading Files
To upload a file simply open Raycast and start tying "upload file" and you should see the Pinata command to upload. You can either upload a file or a folder! After selecting you can select the file and give it a name, then use `Command + Enter` to upload it.
## Listing Files
You can also list your most recent files by opening Raycast and typing "list files." This will return your most recent files where you can search for a CID or file name in particular, as well as:
* Open the file in your web browser with `enter`
* Copy the CID for the file with `Command + Enter`
# Pinata SDK (Archived)
# ATTENTION
This is the old Node.js Pinata SDK that is no longer being maintained. Please check out our new IPFS SDK
## Overview
The Pinata NodeJS SDK provides the quickest / easiest path for interacting with the [Pinata API](/api-reference/introduction).
## Installation
```
npm install --save @pinata/sdk
```
## Setup
Pinata offers developer keys for various scenarios. The most common scenario is normal IPFS actions (pinning content, unpinning, listing content, etc). These actions happen through the core Pinata API, and are documented in the [Pinata API Reference](/api-reference/introduction).
To create an API key, visit the [Keys Page](https://app.pinata.cloud/developers/keys) and click the "New Key" button in the top right. Once you do that you can select if you want your key to be admin or if you want to scope the privileges of the keys to certain endpoints or limit the number of uses. Make those selections, then give the key a name at the bottom, and click create key.
If you are just getting started we recommend using Admin privileges, then move to scope keys as you better understand your needs
Once you have created the keys you will be shown your API Key Info. This will contain your **Api Key**, **API Secret**, and your **JWT**. Click "Copy All" and save them somewhere safe!
The API keys are only shown once, be sure to copy them somewhere safe!
In the example below we provided with 3 ways to call the pinata SDK.
```javascript
// Use the api keys by providing the strings directly
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK('yourPinataApiKey', 'yourPinataSecretApiKey');
```
```javascript
// Use the api keys by specifying your api key and api secret
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK({ pinataApiKey: 'yourPinataApiKey', pinataSecretApiKey: 'yourPinataSecretApiKey' });
```
```javascript
// Use the JWT key
const pinataSDK = require('@pinata/sdk');
const pinata = new pinataSDK({ pinataJWTKey: 'yourPinataJWTKey'});
```
Quickly test that you can connect to the API with the following call:
```javascript
const res = await pinata.testAuthentication()
console.log(res)
// "message": "Congratulations! You are communicating with the Pinata API"!"
```
## Usage
Once you've set up your instance, using the Pinata SDK is easy. Simply call your desired function and handle the results of the promise.
### `hashMetadata`
Allows the user to change the name and keyvalues associated with content pinned to Pinata.
Changes made via this endpoint only affect the metadata for the hash passed in. [Metadata](/pinning/pinata-metadata) is specific to Pinata and does not modify the actual content stored on IPFS in any way. It is simply a convenient way of keeping track of what content you have stored.
##### `pinata.hashMetadata(ipfsPinHash, metadata)`
##### Params
* `ipfsPinHash` - A string for a valid IPFS Hash that you have pinned on Pinata.
* `metadata` A JSON object containing the following:
* `name` (optional) - A new name that Pinata will associate with this particular hash.
* `keyvalues` (optional) - A JSON object with the updated keyvalues you want associated with the hash provided (see more below)
###### Adding or modifying keyvalues
To add or modify existing keyvalues, simply provide them in the following format for the keyvalues object:
```
keyvalues: {
newKey: 'newValue', //this adds a keyvalue pair
existingKey: 'newValue' //this modifies the value of an existing key if that key already exists
}
```
###### Removing keyvalues
To remove a keyvalue pair, simply provide null as the value for an existing key like so:
```
keyvalues: {
existingKeyToRemove: null //this removes a keyvalue pair
}
```
#### Response
If the operation is successful, you will receive back an "OK" REST 200 status.
##### Example Code
```javascript
const metadata = {
name: 'new custom name',
keyvalues: {
newKey: 'newValue',
existingKey: 'newValue',
existingKeyToRemove: null
}
};
const res = await pinata.hashMetadata('yourHashHere', metadata)
console.log(res)
```
### `pinByHash`
Adds a hash to Pinata's pin queue to be pinned asynchronously.
##### `pinata.pinByHash(hashToPin, options)`
##### Params
* `hashToPin` - A string for a valid IPFS Hash (Also known as a CID)
* `options` (optional): A JSON object that can contain following keyvalues:
* `pinataMetadata` (optional): A JSON object with [optional metadata](/pinning/pinata-metadata) for the hash being pinned
* `pinataOptions`
* `hostNodes` (optional): An array of multiaddresses for nodes that are currently hosting the content to be pinned
#### Response
```
{
id: This is Pinata's ID for the pin job,
ipfsHash: This is the IPFS multi-hash provided to Pinata to pin,
status: The current status of the pin job. If the request was successful the status should be 'searching'.
name: The name of the pin (if provided initially)
}
```
##### Example Code
```javascript
const options = {
pinataMetadata: {
name: MyCustomName,
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
hostNodes: [
'/ip4/hostNode1ExternalIP/tcp/4001/ipfs/hostNode1PeerId',
'/ip4/hostNode2ExternalIP/tcp/4001/ipfs/hostNode2PeerId'
]
}
};
const res = await pinata.pinByHash('yourHashHere', options)
console.log(res)
```
### `pinFileToIPFS`
Send a file to Pinata for direct pinning to IPFS.
##### `pinata.pinFileToIPFS(readableStream, options)`
##### Params
* `readableStream` - A [readableStream](https://nodejs.org/api/stream.html) of the file to be added
* `options` (optional): A JSON object that can contain the following keyvalues:
* `pinataMetadata` (optional): A JSON object with [metadata](/pinning/pinata-metadata) for the file being pinned
* `pinataOptions` (optional): A JSON object with additional [options](pinning/pinata-metadata#pinataoptions) for the file being pinned
#### 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)
}
```
##### Example Code
```javascript
const fs = require('fs');
const readableStreamForFile = fs.createReadStream('./yourfile.png');
const options = {
pinataMetadata: {
name: MyCustomName,
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
cidVersion: 0
}
};
const res = await pinata.pinFileToIPFS(readableStreamForFile, options)
console.log(res)
```
### `pinFromFS`
Read from a location on your local file system and recursively pin the contents to IPFS (node.js only).
Both individual files, as well as directories can be read from.
##### `pinata.pinFromFS(sourcePath, options)`
##### Params
* `sourcePath` - The location on your local filesystem that should be read from.
* `options` (optional): A JSON object that can contain the following keyvalues:
* `pinataMetadata` (optional): A JSON object with [metadata](/pinning/pinata-metadata) for the file being pinned
* `pinataOptions` (optional): A JSON object with additional [options](/pinning/pinata-metadata#pinataoptions) for the file being pinned
#### 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)
}
```
##### Example Code
```javascript
const sourcePath = '/Users/me/builds/my-awesome-website/';
const options = {
pinataMetadata: {
name: 'My Awesome Website',
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
cidVersion: 0
}
};
const res = await pinata.pinFromFS(sourcePath, options)
console.log(res)
```
### `pinJobs`
This endpoint allows users to search for the status of all hashes that are currently in Pinata's pin queue. Records in the pin queue arrive there through the [pinByHash](/api-reference/endpoint/pin-by-cid) operation.
##### `pinata.pinJobs(filters)`
##### Params
* `filters` (optional): An object that can consist of the following optional query parameters:
* `sort` (optional): How you wish for the records in the response to be sorted. Valid inputs for this are:
* `'ASC'`
* `'DESC'`
* `status` (optional): What the current status of the record is in the pin queue. Valid statuses and their meanings are:
* `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` - The provided host node(s) were either invalid or unreachable. Please make sure all provided host nodes are online and reachable.
* `ipfs_pin_hash` (optional): A string for a valid IPFS hash (also known as a CID) to search for
* `limit` (optional): Limit the amount of results returned per page of results (default is 5, and max is also 1000)
* `offset` (optional): Provide the record offset for records being returned. This is how you retrieve records on additional pages (default is 0)
#### Response
```
{
count: (this is the total number of pin job records that exist for the query filters you passed in),
rows: [
{
id: (the id for the pin job record),
ipfs_pin_hash: (the IPFS multi-hash for the content you pinned),
date_queued: (The date this hash was initially queued to be pinned - represented in ISO 8601 format),
name: (If you passed in a name for your hash, it will be listed here),
status: (The current status for the pin job)
},
{
same record format as above
}
.
.
.
]
}
```
##### Example Code
```javascript
const filters = {
sort: 'ASC',
status: 'searching',
ipfs_pin_hash: 'Qma6e8dovfLyiG2UUfdkSHNPAySzrWLX9qVXb44v1muqcp',
limit: 10,
offset: 0
};
const res = await pinata.pinJobs('yourHashHere', filters)
console.log(res)
```
### `pinJSONToIPFS`
Send JSON to Pinata for direct pinning to IPFS.
##### `pinata.pinJSONToIPFS(body, options)`
##### Params
* `body` - Valid JSON you wish to pin to IPFS
* `options` (optional): A JSON object that can contain the following keyvalues:
* `metadata` (optional): A JSON object with [metadata](/pinning/pinata-metadata) for the hash being pinned
* `pinataOptions` (optional): A JSON object with additional [options](/pinning/pinata-metadata#pinataoptions) for the JSON being pinned
#### 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)
}
```
##### Example Code
```javascript
const body = {
message: 'Pinatas are awesome'
};
const options = {
pinataMetadata: {
name: MyCustomName,
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
cidVersion: 0
}
};
const res = await pinata.pinJSONToIPFS(body, options)
console.log(res)
```
### `unpin`
Have Pinata unpin content that you've pinned through the service.
##### `pinata.unpin(hashToUnpin)`
##### Params
* `hashToUnpin` - the hash of the content you wish to unpin from Pinata
#### Response
If the operation is successful, you will simply receive "OK" as your result
##### Example Code
```javascript
const res = await pinata.unpin(hashToUnpin)
console.log(res)
```
### `testAuthentication`
Tests that you can authenticate with Pinata correctly
##### `pinata.testAuthentication()`
##### Params
None
#### Response
```
{
authenticated: true
}
```
##### Example Code
```javascript
const res = await pinata.testAuthentication()
console.log(res
```
### `pinList`
Retrieve pin records for your Pinata account. In order to get the next page you have to manipulate `pageLimit` and `pageOffset` filter to get the next page. This method no longer return the total count of pins. We highly encourage you to use the auto-pagination method [getFilesByCount]().
##### `pinata.pinList(filters)`
##### Params
* `filters` (optional): An object that can consist of the following optional query parameters:
* `hashContains` (optional): A string of alphanumeric characters that desires hashes must contain
* `pinStart` (optional): The earliest date the content is allowed to have been pinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `pinEnd` (optional): The earliest date the content is allowed to have been pinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `unpinStart` (optional): The earlist date the content is allowed to have been unpinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `unpinEnd` (optional): The latest date the content is allowed to have been unpinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `pinSizeMin` (optional): The minimum byte size that pin record you're looking for can have
* `pinSizeMax` (optional): The maximum byte size that pin record you're looking for can have
* `status` (optional): Filter pins using one of the following options
* `'all'` (Records for both pinned and unpinned content will be returned)
* `'pinned'` (Only records for pinned content will be returned)
* `'unpinned'` (Only records for unpinned content will be returned)
* `pageLimit` (optional): Limit the amount of results returned per page of results (default is 10, and max is 1000)
* `pageOffset` (optional): Provide the record offset for records being returned. This is how you retrieve records on additional pages (default is 0)
* `metadata` (optional): A JSON object that can be used to find records for content that had optional metadata included when it was added to Pinata. The metadata object is formatted as follows:
##### Metadata filter object formatting
```
{
name: 'exampleName',
keyvalues: {
testKeyValue: {
value: 'exampleFilterValue',
op: 'exampleFilterOperation'
},
testKeyValue2: {
value: 'exampleFilterValue2',
op: 'exampleFilterOperation2'
}
}
}
```
Filter explanations:
* `name` (optional): If provided, any records returned must have a name that contains the string provided for the 'name'.
* `keyvalues` (optional): Each keyvalue provided in this object have both a `value` and `op`
* `value` (required): This is the value which will be filtered on
* `op` (required): This is the filter operation that will be applied to the `value` that was provided. Valid op values are:
* `'gt'` (greater than the value provided)
* `'gte'` (greater than or equal to the value provided)
* `'lt'` (less than the value provided)
* `'lte'` (less than or equal to the value provided)
* `'ne'` (not equal to the value provided)
* `'eq'` (equal to the value provided)
* `'between'` (between the two values provided) - NOTE - This also requires a `secondValue` be provided as seen in the example below
* `'notBetween'` (not between the two values provided) - NOTE - This also requires a `secondValue` be provided as seen in the example below
* `'like'` (like the value provided)
* `'notLike'` (not like the value provided)
* `'iLike'` (case insensitive version of `like`)
* `'notILike'` (case insensitive version of `notLike`)
* `'regexp'` (filter the value provided based on a provided regular expression)
* `'iRegexp'` (case insensitive version of regexp)
As an example, the following filter would only find records whose name contains the letters 'invoice', have the metadata key 'company' with a value of 'exampleCompany', and have a metadata key 'total' with values between 500 and 1000:
```
{
name: 'invoice',
keyvalues: {
company: {
value: 'exampleCompany,
op: 'eq'
},
total: {
value: 500,
secondValue: 1000,
op: 'between'
}
}
}
```
Our libraries support auto-pagination. This feature easily handles fetching large lists of resources without having to manually paginate results and perform subsequent requests.
To use the auto-pagination feature in Node 10+, simply iterate over a "list" call with the parameters you need in a for await loop.
### `getFilesByCount`
This method support auto-pagination. This feature easily handles fetching large lists of pin records for your Pinata account without having to manually paginate results and perform subsequent requests. To use the auto-pagination feature in Node 10+.
##### `pinata.getFilesByCount(filters, count)`
##### Params
* `filters` (optional): An object that can consist of the following optional query parameters:
* `hashContains` (optional): A string of alphanumeric characters that desires hashes must contain
* `pinStart` (optional): The earliest date the content is allowed to have been pinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `pinEnd` (optional): The earliest date the content is allowed to have been pinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `unpinStart` (optional): The earlist date the content is allowed to have been unpinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `unpinEnd` (optional): The latest date the content is allowed to have been unpinned. Must be a valid [ISO\_8601](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) date.
* `pinSizeMin` (optional): The minimum byte size that pin record you're looking for can have
* `pinSizeMax` (optional): The maximum byte size that pin record you're looking for can have
* `status` (optional): Filter pins using one of the following options
* `'all'` (Records for both pinned and unpinned content will be returned)
* `'pinned'` (Only records for pinned content will be returned)
* `'unpinned'` (Only records for unpinned content will be returned)
* `metadata` (optional): A JSON object that can be used to find records for content that had optional metadata included when it was added to Pinata. The metadata object is formatted as follows:
* `count` (optional): A Number that specifies how many pins you want. if no number is provided then it will pull all the pins based on your filter
##### Metadata filter object formatting
```
{
name: 'exampleName',
keyvalues: {
testKeyValue: {
value: 'exampleFilterValue',
op: 'exampleFilterOperation'
},
testKeyValue2: {
value: 'exampleFilterValue2',
op: 'exampleFilterOperation2'
}
}
}
```
Filter explanations:
* `name` (optional): If provided, any records returned must have a name that contains the string provided for the 'name'.
* `keyvalues` (optional): Each keyvalue provided in this object have both a `value` and `op`
* `value` (required): This is the value which will be filtered on
* `op` (required): This is the filter operation that will be applied to the `value` that was provided. Valid op values are:
* `'gt'` (greater than the value provided)
* `'gte'` (greater than or equal to the value provided)
* `'lt'` (less than the value provided)
* `'lte'` (less than or equal to the value provided)
* `'ne'` (not equal to the value provided)
* `'eq'` (equal to the value provided)
* `'between'` (between the two values provided) - NOTE - This also requires a `secondValue` be provided as seen in the example below
* `'notBetween'` (not between the two values provided) - NOTE - This also requires a `secondValue` be provided as seen in the example below
* `'like'` (like the value provided)
* `'notLike'` (not like the value provided)
* `'iLike'` (case insensitive version of `like`)
* `'notILike'` (case insensitive version of `notLike`)
* `'regexp'` (filter the value provided based on a provided regular expression)
* `'iRegexp'` (case insensitive version of regexp)
#### Response
```
[
{
id: (the id of your pin instance record),
ipfs_pin_hash: (the IPFS multi-hash for the content you pinned),
size: (this is how large (in bytes) the content pinned is),
user_id: (this is your user id for Pinata),
date_pinned: (This is the timestamp for when this content was pinned - represented in ISO 8601 format),
date_unpinned: (This is the timestamp for when this content was unpinned (if null, then you still have the content pinned on Pinata),
metadata: {
name: (this will be the name of the file originally uploaded, or the custom name you set),
keyvalues: {
exampleCustomKey: "exampleCustomValue",
exampleCustomKey2: "exampleCustomValue2", ...
}
}
},
{ same record format as above } . . .
]
```
```
const pinataSDK = require('@pinata/sdk');
const pinata = pinataSDK('yourPinataApiKey', 'yourPinataSecretApiKey');
const metadataFilter = {
name: 'exampleName',
keyvalues: {
testKeyValue: {
value: 'exampleFilterValue',
op: 'exampleFilterOperation'
},
testKeyValue2: {
value: 'exampleFilterValue2',
op: 'exampleFilterOperation2'
}
}
};
const filters = {
status : 'pinned',
pageLimit: 10,
pageOffset: 0,
metadata: metadataFilter
};
// more reference at [for await ...](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of)
for await (const item of pinata.getFilesByCount(filters, 35)) {
// ...(item) perform any task with the current item in the array of 35
}
for await (const item of pinata.getFilesByCount(filters)) {
// ...(item) perform any task with the current item the array is determined by all your pins
}
```
### `userPinnedDataTotal`
Returns the total combined size (in bytes) of all content you currently have pinned on Pinata.
##### `pinata.userPinnedDataTotal()`
##### Params
None
#### Response
The response for this call will the total combined size of everything you currently have pinned on pinata.
This value will be expressed in bytes
##### Example Code
```javascript
const res = await pinata.userPinnedDataTotal()
console.log(res)
```
## Host Node Multiaddresses
For endpoints that involve Pinata finding and pinning content that already resides on the IPFS network, you can help Pinata find your content faster by optionally providing us with the "multiaddresses" up to five host nodes that your content already resides on.
To find the multiaddresses of your nodes, simply run the following on your node's command line:
```
ipfs id
```
In the response, you'll want to focus on the "Addresses" array that's returned. Here you'll find the multiaddresses of your node. These multiaddresses are what other IPFS nodes use to connect to your node.
In the "Addresses" array, take note of the multiaddress that contains your external IP address. Not the local ipv4 "127.0.0.1" address or the local ipv6 "::1" address.
Here's an example of what a full external ipv4 multiaddress would look like (your IP address and node ID will differ):
`/ip4/123.456.78.90/tcp/4001/ipfs/QmAbCdEfGhIjKlMnOpQrStUvWxYzAbCdEfGhIjKlMnOpQr`
⚠️ Please make sure every node provided is online. Pinata will attempt to connect to all nodes before pinning the content, and if any these nodes are offline, your request will eventually fail.
## Pin Policies (deprecated)
A pin policy tells Pinata how many times content should be replicated, and where that content should be replicated at.
Pin policies take the following form:
##### Example pin policy object
```
{
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)
## Questions? Issues? Suggestions?
Feel free to file a Github issue or email us at [team@pinata.cloud](mailto:team@pinata.cloud)
We'd love to hear from you!