OpenSea NFT Metadata Format Explained, With a Real Example

By Puffles StudioPublished Updated

OpenSea-style NFT metadata is one small JSON file per NFT with a name, a description, an image address and a list of attributes (its traits). The NFT’s contract points to each file, and marketplaces read it to show the picture and the traits.

What NFT metadata is

An NFT’s name, picture and traits live in a small JSON file (plain text that lists names and values). The NFT’s contract hands out an address for each token: for ERC-721, the common standard for one-of-a-kind NFTs on Ethereum and similar chains, a function called tokenURI returns the address of that token’s file. OpenSea “reads token metadata from the URI returned by your ERC-721 tokenURI function” (OpenSea).

A generator such as Puffles Studio writes these files for you, one per NFT, next to the images.

The fields

The ERC-721 standard’s own metadata schema has three fields: name, description and image (EIP-721). OpenSea reads more. These are the fields on its Media and traits page, updated 21 July 2026:

OpenSea’s metadata fields, and which ones Puffles Studio writes
FieldWhat it holdsIn the ERC-721 schemaWritten by Puffles Studio
nameThe NFT’s nameYesYes: your NFT name and a number, like “Puff Editions #1”
descriptionText about the NFT (OpenSea supports Markdown)YesOnly if you wrote one
imageThe address of the pictureYesYes: your images folder address plus the file name
attributesThe traits, as trait_type and value pairsNoYes: one per trait, with None left out
external_urlA link to your own siteNoNo
animation_urlA video, audio, 3D or web page fileNoNo
background_colorA six-character hex colour, without #NoNo

The same page says to “use images at least 3000 by 3000 pixels”. Puffles Studio can export at any width from 64 to 8,192 pixels, or at your traits’ own size.

A real file, field by field

This is json/1.json from the Puff Editions starter kit, exactly as the studio’s planner writes it (100 NFTs, seed “puff-editions”, no image address set yet):

{
  "name": "Puff Editions #1",
  "description": "An original collection. Created with Puffles Studio.",
  "image": "ipfs://REPLACE_WITH_IMAGES_CID/1.png",
  "attributes": [
    { "trait_type": "Background", "value": "Mist" },
    { "trait_type": "Body", "value": "Frost" },
    { "trait_type": "Expression", "value": "Happy" },
    { "trait_type": "Headwear", "value": "Bucket" }
  ]
}
  • name: the NFT name (the collection name, unless you set another) and its number.
  • description: the collection’s description, copied to every NFT. Leave it empty and the field is left out.
  • image: your images folder address, then the file name. REPLACE_WITH_IMAGES_CID is a placeholder until you set the address.
  • attributes: one entry per trait folder. The folder name becomes the trait_type, without the number in front (“04_Headwear” becomes “Headwear”), and the file name becomes the value.

The very next file, json/2.json, has no hat: its Headwear was the None choice, so its attributes list has three entries, not four.

File numbers and name numbers

Puffles Studio numbers files and names separately, in the Metadata tab. “File numbering starts at” sets the number of the image and metadata files; “Name numbering starts at” sets the number people read. Each can start at any whole number from 0 to 1,000,000, and both start at 1 unless you change them.

Starting at 0 or 1

Some contracts number tokens from 0 and others from 1. Match the file numbering to your contract: the file number is the token number.

A file called 0.png named “#1”

Set files to start at 0 and names at 1, and the first NFT’s files are 0.png and 0.json while its name is “Puff Editions #1”. With a real images folder address, the start of the file reads:

{
  "name": "Puff Editions #1",
  "description": "An original collection. Created with Puffles Studio.",
  "image": "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/0.png",

With or without .json on the end

The Metadata tab’s “Metadata file extension” setting saves the files as 1.json or simply 1. Whether your contract expects the extension depends on how it builds each token’s address, so pick whichever it uses. The addresses inside the files are the same either way.

_metadata.json and the per-NFT files

The export also holds _metadata.json: every NFT’s metadata in one file, in collection order. The json/ folder is what you upload for the contract to point at. _metadata.json is handy for checking the whole collection at once, or for another tool.

The image address

Until you set it, every image field starts with the placeholder ipfs://REPLACE_WITH_IMAGES_CID. Upload your images folder first, then put the folder’s address (ipfs://…, https://… or ar://…) into the metadata. You don’t need to build the images again: set your NFT image address shows the three ways.

Fields Puffles does not write

external_url, animation_url, background_color and number or date traits (OpenSea’s display_type) are not written. Trait values come from file names, so they are always text, and OpenSea treats “a numeric value wrapped in quotes” as text too. To add one of these fields, edit the JSON files after export and keep _metadata.json in step.

Puffles Studio writes metadata in this EVM, OpenSea style only. For the spreadsheet OpenSea’s drops use instead of JSON, see the drop CSV.

Questions people ask

Is attributes required?

Not by the ERC-721 standard, which defines only name, description and image. But marketplaces read attributes to show and filter traits, so generative collections nearly always have them.

Why does my image say REPLACE_WITH_IMAGES_CID?

It’s a placeholder: your images folder has no address until you upload it. Set the address in the studio and download the metadata again (how).

Should metadata files end in .json?

Only if your contract adds .json when it builds each token’s address. Puffles Studio can save them either way.

Can I start numbering at 0?

Yes. File numbering and name numbering each start at any whole number from 0 to 1,000,000, so 0.png can be named “#1”.

Can I add an external_url?

Not in the studio today. Add it to the JSON files after export if you need it. The OpenSea drop CSV has an external_url column, which Puffles leaves empty.