1 Overview
Some clients want to look like Instagram, Flickr, Snapchat, or 9GAG. In those apps the picture is the post. Text is secondary. This NIP defines event kind 20, the picture event, for exactly that use case.
A picture event must be self-contained. The relay does not store image bytes. The images are hosted externally. The event references each image with an imeta tag (see NIP-92). Each imeta tag carries the URL, the media type, dimensions, hashes, alt text, and fallback URLs. A client can render the whole post from the event alone, then fetch the pixels.
One picture event may reference multiple images. A client displays them together as a single post, like a photo carousel. Picture feeds can also mix in NIP-71 kind 22 events to show short vertical videos in the same stream.
Why a new kind and not a kind-1 note with an image link? A dedicated kind lets clients filter for picture posts directly: {"kinds": [20]}. No text-note noise, no guessing which URLs are images.
imeta tag.2 The Picture Event (kind 20)
A picture event has a title tag and a description in content. It follows the standard NIP-01 event format. Kind 20 falls in the regular range, so relays store every picture event.
{
"id": <32-byte lowercase hex sha256 of the serialized event>,
"pubkey": <32-byte lowercase hex public key of the creator>,
"created_at": <unix timestamp, seconds>,
"kind": 20,
"content": "<description of the post>",
"tags": [
["title", "<short title of the post>"],
// one imeta tag per image (see §3)
["imeta", "url …", "m image/jpeg", "thumbhash …", "blurhash …",
"dim 3024x4032", "alt …", "x <sha256>", "fallback …", "fallback …"],
["imeta", "url …", …, "annotate-user <pubkey>:<posX>:<posY>"],
["content-warning", "<reason>"], // if NSFW
["p", "<pubkey>", "<relay URL, optional>"], // tagged users
["m", "image/jpeg"], // media type, for filters
["x", "<sha256>"], // hash of each image, queryable
["t", "<hashtag>"],
["location", "<location>"], // city, state, country
["g", "<geohash>"],
["L", "ISO-639-1"], // language of text in the image
["l", "en", "ISO-639-1"]
]
}
| Tag | Value | Rule |
|---|---|---|
| title | string | Short title of the post. The long description lives in content. |
| imeta | key-value pairs | One per image. Carries all metadata for that image. See §3. |
| content-warning | reason | Add this tag when the post is NSFW. Clients hide the picture until the user opts in (see NIP-36). |
| p | pubkey, optional relay URL | A tagged user. One tag per user, as in NIP-01. |
| m | media type | Repeats the media type at event level. Lets clients filter with {"#m": [...]} for kinds they can render. |
| x | sha256 hex | Repeats each image hash at event level. Makes images queryable with {"#x": [...]}. |
| t | hashtag | A hashtag. One tag per hashtag. |
| location | location string | Human-readable place: city name, state, country. |
| g | geohash | Machine-readable position of the post. |
| L | ISO-639-1 | Label namespace (see NIP-32). Add it when text is written in the image. |
| l | language code, ISO-639-1 | The language of the text in the image, for example en. |
3 The imeta Tag, Per Image
Each image gets one imeta tag. Every element after the tag name is a space-separated key-value string. The format comes from NIP-92; the hash and metadata fields come from NIP-94.
| Key | Value | Rule |
|---|---|---|
| url | image URL | The primary location of the image on an external host. |
| m | media type | MIME type of the image. Must be one of the six accepted types in §4. |
| thumbhash | base64 string | A compact placeholder. Clients render it while the image loads. |
| blurhash | blurhash string | An alternative compact placeholder with the same purpose. |
| dim | WxH | Pixel dimensions, for example 3024x4032. Prevents layout shift. |
| alt | string | Accessibility description of the image. |
| x | sha256 hex | Hash of the image file, as specified in NIP-94. Verifies the download. |
| fallback | image URL | A mirror of the same file. The key may repeat for multiple mirrors. |
| annotate-user | <pubkey>:<posX>:<posY> | Places a user link at a specific position in the picture. The pubkey is 32-byte hex. |
url first, then each fallback mirror. The x hash proves every mirror served the same file.4 Accepted Media Types
Picture events accept only the following media types. Reject other types.
| MIME type | Format |
|---|---|
| image/apng | Animated Portable Network Graphics (APNG) |
| image/avif | AV1 Image File Format (AVIF) |
| image/gif | Graphics Interchange Format (GIF) |
| image/jpeg | Joint Photographic Expert Group image (JPEG) |
| image/png | Portable Network Graphics (PNG) |
| image/webp | Web Picture format (WEBP) |
No image/svg+xml, no video/*. For short vertical videos in the same feed, use NIP-71 kind 22 events instead.
The schema block in the source spec is fenced as YAML but written as JSON with comments, and it is missing a comma after the event-level x tag. Treat it as illustrative, not parseable. The source also uses imeta without citing NIP-92, where the tag is defined.
5 Worked Example
A photographer posts two coast photos as one carousel. The second photo tags a friend standing in the frame. The event is publishable as-is: valid JSON, all metadata inline.
{
"id": "7ce4b58c21…",
"pubkey": "a3d1e0f492…",
"created_at": 1749762600,
"kind": 20,
"content": "Two mornings on the Pacific side. No filter.",
"tags": [
["title", "Costa Rica coast"],
["imeta",
"url https://nostr.build/i/my-image.jpg",
"m image/jpeg",
"thumbhash zOcFFIIKmXdCinaXaHcmcHUFSA==",
"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$",
"dim 3024x4032",
"alt A scenic photo overlooking the coast of Costa Rica",
"x 8a2f04c1…",
"fallback https://nostrcheck.me/alt1.jpg",
"fallback https://void.cat/alt1.jpg"],
["imeta",
"url https://nostr.build/i/my-image2.jpg",
"m image/jpeg",
"thumbhash zOcFFIIKmXgzmWaXWIcmcFQEGA",
"dim 3024x4032",
"alt Another scenic photo overlooking the coast of Costa Rica",
"x 61bb90d3…",
"fallback https://nostrcheck.me/alt2.jpg",
"fallback https://void.cat/alt2.jpg",
"annotate-user f7234bd4c1…:0.62:0.41"],
["p", "f7234bd4c1…", "wss://relay.example.com"],
["m", "image/jpeg"],
["x", "8a2f04c1…"],
["x", "61bb90d3…"],
["t", "photography"],
["t", "costarica"],
["location", "Santa Teresa, Puntarenas, Costa Rica"],
["g", "d1u0q"]
],
"sig": "90ab44cd12…"
}
A picture-first client builds its feed with one subscription. It asks for pictures and short videos together:
→ ["REQ", "picfeed", {"kinds":[20,22], "limit":30}] ← ["EVENT", "picfeed", {"kind":20, "tags":[["title","Costa Rica coast"], …], …}] ← ["EVENT", "picfeed", {"kind":22, …}] // NIP-71 short video, same feed ← ["EOSE", "picfeed"] -- only JPEG-capable? filter by media type instead -- → ["REQ", "jpegs", {"kinds":[20], "#m":["image/jpeg"], "limit":30}] -- find every post that contains one exact image file -- → ["REQ", "byhash", {"kinds":[20], "#x":["8a2f04c1…"]}]
6 Version History
NIP-68 arrived in late 2024 and stabilized within weeks. Notable changes, newest first:
- 20262026-04Documented the optional
thumbhashplaceholder alongsideblurhash. - 20252025-01Video companion updated: NIP-71 made video events regular, and the same-feed reference became kind
22. - 20242024-11Added the
xtag so posts are indexable by image sha256; simplified the text. - 20242024-10Added
locationand geohash tags, andL/llanguage labels for text written in images. - 20242024-10Restricted posts to the six accepted media types.
- Origin2024-10-27Vitor Pamplona proposed kind 20 under the working title "Instagram feeds".
Original Proposal - Credits
| Author | Vitor Pamplona |
| First commit | 2024-10-27 — “Instagram feeds” |
| Status | draft - optional — only picture-first clients need it |
| Source | 68.md on GitHub - full change history |
7 See Also
- NIP-01 — the base event format, kinds, and the
ptag used here. - NIP-92 — defines the
imetatag that carries all per-image metadata. - NIP-94 — file metadata; the source of the
xsha256 hash convention. - NIP-71 — video events; kind
22short videos share the picture feed. - NIP-36 — sensitive content; the
content-warningtag used for NSFW posts. - NIP-32 — labeling; the
L/lnamespace used for image languages.