1 Overview
The world already has global content identifiers. A book has an ISBN. A podcast feed has a GUID. A movie has an ISAN. A paper has a DOI. This NIP lets an event reference these identifiers in a standard way.
An event adds an i tag with the external identifier. It adds a k tag with the identifier kind. Relays index single-letter tags (NIP-01). So a client can query one filter and get every event about one book, one episode, or one transaction.
This NIP defines no event kind of its own. Any event kind may carry i and k tags. Comment threads on external content (NIP-22) are the main consumer.
Think of the i tag as a library card catalog number. The k tag names the catalog. Query {"#i": [<id>]} for one item. Query {"#k": [<kind>]} for the whole catalog.
i tag. The relay index makes the identifier queryable.2 The i and k Tags
Use the i tag to reference an external content identifier. Use the k tag to state the identifier kind. The k value is a fixed string per identifier type. It lets clients query all events for a whole type, not just one item.
["i", <external id, formatted per §3>, <url hint, optional — see §5>] ["k", <external id kind, e.g. "isbn", "web", "podcast:guid">]
Both tags are single-letter tags. Relays index their first value (NIP-01). Clients query them with the #i and #k filter keys.
3 Supported ID Types
The table lists every supported identifier type. The i tag value must follow the exact format shown. The k tag value is the fixed string in the last column.
| Type | i tag value | k tag value |
|---|---|---|
| URLs | <URL, normalized, no fragment> | web |
| Books | isbn:<id, without hyphens> | isbn |
| Geohashes | geo:<geohash, lowercase> | geo |
| Countries | iso3166:<code, uppercase> | iso3166 |
| Movies | isan:<id, without version part> | isan |
| Papers | doi:<id, lowercase> | doi |
| Hashtags | #<topic, lowercase> | # |
| Podcast Feeds | podcast:guid:<guid> | podcast:guid |
| Podcast Episodes | podcast:item:guid:<guid> | podcast:item:guid |
| Podcast Publishers | podcast:publisher:guid:<guid> | podcast:publisher:guid |
| Blockchain Transaction | <blockchain>:[<chainId>:]tx:<txid, hex, lowercase> | <blockchain>:tx |
| Blockchain Address | <blockchain>:[<chainId>:]address:<address> | <blockchain>:address |
The source spec says URLs are "normalized" but defines no normalization algorithm. Only "no fragment" is explicit. Two clients may normalize the same URL differently and miss each other's events. Also note: the hashtag form here overlaps with the t tag from NIP-24; the spec does not say how the two relate.
URLs (webpages)
Use the normalized URL with the fragment removed. The k tag is the hardcoded string web. For the page https://myblog.example.com/post/2012-03-27/hello-world, the tags are:
[ ["i", "https://myblog.example.com/post/2012-03-27/hello-world"], ["k", "web"] ]
Books (ISBN)
A book identifier is isbn: plus the ISBN without hyphens. Example: ["i", "isbn:9780765382030"]. Many book search APIs return ISBNs without hyphens. Removing hyphens is trivial. Adding them back needs a library. So the hyphen-free form is the canonical one.
Geohashes
A geohash encodes a geographic location as a short string of letters and digits. The identifier is geo: plus the geohash. Geohashes must be lowercase. Example: ["i", "geo:ezs42e44yx96"].
Countries and subdivisions (ISO 3166)
ISO 3166 codes reference countries (ISO 3166-1 alpha-2) or subdivisions such as states and provinces (ISO 3166-2). The codes must be uppercase.
- Country (Venezuela):
["i", "iso3166:VE"] - Subdivision (California, USA):
["i", "iso3166:US-CA"]
Movies (ISAN)
A movie identifier is isan: plus the ISAN without the version part. Versions and edits of a movie are not relevant here. Example: ["i", "isan:0000-0000-401A-0000-7"].
Papers (DOI)
A paper identifier is doi: plus the DOI in lowercase. The k tag is doi.
Hashtags
A hashtag identifier is # plus the topic in lowercase. The k tag is the single character #.
Podcasts
Podcast identifiers reuse the GUIDs from the podcast namespace. Three levels exist:
- Feed:
["i", "podcast:guid:c90e609a-df1e-596a-bd5e-57bcc8aad6cc"] - Episode (RSS item):
["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f"] - Publisher:
["i", "podcast:publisher:guid:18bcbf10-6701-4ffb-b255-bc057390d738"]
4 Blockchain IDs
<blockchain> can be any layer-1 chain: bitcoin, ethereum, solana, and so on. When one name covers many chains — for example the EVM chains under ethereum — insert a numeric <chainId> segment. The k tag never includes the chain id. It is always <blockchain>:tx or <blockchain>:address.
i tag. The k tag stays chain-id-free.Bitcoin
A txid is hex and lowercase. An address is bech32 lowercase, or base58 with its case preserved:
bitcoin:address:<bech32, lowercase | base58, case sensitive> bitcoin:tx:<txid, hex, lowercase> [ ["i", "bitcoin:tx:a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"], ["k", "bitcoin:tx"] ] [ ["i", "bitcoin:address:1HQ3Go3ggs8pFnXuHVHRytPCq5fGG8Hbhx"], ["k", "bitcoin:address"] ]
Ethereum and other EVM chains
EVM identifiers carry an integer chain id. Transaction hashes and addresses are hex and lowercase — lowercase the checksummed address before you tag it:
ethereum:<chainId, integer>:tx:<txHash, hex, lowercase> ethereum:<chainId, integer>:address:<hex, lowercase> -- mainnet address (chainId 1) -- [ ["i", "ethereum:1:address:0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], ["k", "ethereum:address"] ] -- Gnosis chain transaction (chainId 100) -- [ ["i", "ethereum:100:tx:0x98f7812be496f97f80e2e98d66358d1fc733cf34176a8356d171ea7fbbe97ccd"], ["k", "ethereum:tx"] ]
5 Optional URL Hints
Each i tag may carry a URL hint as its second value. The hint sends people to a website when the client has no opinion about how to open the identifier:
["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"] ["i", "isan:0000-0000-401A-0000-7", "https://www.imdb.com/title/tt0120737"]
In the source spec, both URL-hint examples print the hint URL without quote marks. As written they are not valid JSON. The examples above restore the quotes. The intent — a plain URL string as the third tag element — is not in doubt.
Only the first tag value is indexed by relays. Do not put data you need to query into the hint. The hint is a courtesy link, nothing more.
6 Worked Example
A user reviews a book in a kind-1 note. The note tags the ISBN. A second client later queries every event about that book. Orange lines go to the relay. Blue lines come from the relay.
-- publish a note about a book -- → ["EVENT", {"id":"7fe19a3c…","pubkey":"9d2f04b1…","created_at":1749772800, "kind":1, "tags":[["i","isbn:9780765382030","https://isbnsearch.org/isbn/9780765382030"], ["k","isbn"]], "content":"Finished this one in two nights. Read it.", "sig":"c02b1745…"}] ← ["OK", "7fe19a3c…", true, ""] -- another client asks: all events about this book -- → ["REQ", "book", {"#i": ["isbn:9780765382030"]}] ← ["EVENT", "book", {"id":"7fe19a3c…", "kind":1, "content":"Finished this one…", …}] ← ["EOSE", "book"] → ["CLOSE", "book"] -- or: every event that references any book -- → ["REQ", "shelf", {"#k": ["isbn"], "limit": 50}]
7 Version History
Oscar Merry drafted this NIP for podcast apps. It grew into a general identifier registry. Notable changes, newest first:
- 20262026-01Added ISO 3166 country and subdivision codes.
- 20252025-04Added blockchain transaction and address identifiers with optional chain ids.
- 20252025-03Hardcoded the webpage
ktag to the stringweb. - 20242024-09Expanded the supported id list beyond the original media types.
- 20242024-08Adopted the NIP-73 number; added podcast publisher GUIDs.
- 20242024-06Added the optional URL hint as a second
itag value. - 20242024-05Removed the media type prefix; switched movies to ISAN with real examples.
- Origin2024-04Oscar Merry drafted "External Content IDs".
Original Proposal - Credits
| Author | Oscar Merry |
| First commit | 2024-04-19 — “Draft External Content IDs” (took the number 73 on 2024-08-05) |
| Status | draft - optional |
| Source | 73.md on GitHub - full change history |