{"openapi":"3.1.0","info":{"title":"nostr.build GIF search","version":"1.0.0","description":"GIF search for registered Nostr clients. Web clients are identified by Origin; native apps by API key or User-Agent. Show \"GIFs from nostr.build\", linked to https://nostr.build, wherever these GIFs appear — for example in your GIF picker's header. Registering a client costs 1000 sats, once. The fee only keeps spam sign-ups out — it isn't profit and doesn't fund the site. It isn't refunded if you delete the client. Building a GIF button or picker? Read the integration guide for AI agents: https://gifs.nostr.build/developers/llms-full.txt"},"externalDocs":{"description":"Integration guide (for AI agents and people): identification, previews, posting, errors, picker UX","url":"https://gifs.nostr.build/developers/llms-full.txt"},"components":{"schemas":{"Gif":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","description":"The original, to post in a note."},"width":{"type":"integer"},"height":{"type":"integer"},"frames":{"type":["integer","null"]},"duration":{"type":["number","null"]},"bytes":{"type":["integer","null"]},"format":{"type":"string","enum":["gif","webp"]},"title":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"lqip":{"type":["string","null"]},"mp4":{"type":["string","null"],"description":"The animation as H.264 MP4 at its own size, many times smaller than the GIF; for grids. No audio and no loop flag: play it with `<video loop muted autoplay playsinline>`. Transparency is shown on black. null when it cannot be converted (a still, over 60 s, 1800 frames or 4096 px)."},"previews":{"type":"object","properties":{"small":{"type":"object","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"animated":{"type":["string","null"],"description":"Animated WebP; null when the GIF is too large to animate — show `still`."},"still":{"type":"string","description":"First frame, PNG."}},"required":["width","height","animated","still"],"description":"Height-fitted (96 px tall), for rows."},"medium":{"type":"object","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"animated":{"type":["string","null"],"description":"Animated WebP; null when the GIF is too large to animate — show `still`."},"still":{"type":"string","description":"First frame, PNG."}},"required":["width","height","animated","still"],"description":"Height-fitted (192 px tall), for rows."},"w240":{"type":"object","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"animated":{"type":["string","null"],"description":"Animated WebP; null when the GIF is too large to animate — show `still`."},"still":{"type":"string","description":"First frame, PNG."}},"required":["width","height","animated","still"],"description":"Width-fitted (at most 240 px wide, never enlarged), for column grids: e.g. w240 below 640 px viewports, w480 from 640 px."},"w480":{"type":"object","properties":{"width":{"type":"integer"},"height":{"type":"integer"},"animated":{"type":["string","null"],"description":"Animated WebP; null when the GIF is too large to animate — show `still`."},"still":{"type":"string","description":"First frame, PNG."}},"required":["width","height","animated","still"],"description":"Width-fitted (at most 480 px wide, never enlarged), for column grids: e.g. w240 below 640 px viewports, w480 from 640 px."}},"required":["small","medium","w240","w480"]}},"required":["id","url","width","height","frames","duration","bytes","format","title","tags","lqip","mp4","previews"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]}},"parameters":{}},"paths":{"/api/v1/search":{"get":{"summary":"Search GIFs","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":500,"description":"What to find: words in any language, emoji, or both. An emoji is searched by its meaning (😂 finds laughing faces, 🇺🇸 the flag; skin tones are ignored). Punctuation and search syntax are ignored; the first 100 characters are searched."},"required":true,"description":"What to find: words in any language, emoji, or both. An emoji is searched by its meaning (😂 finds laughing faces, 🇺🇸 the flag; skin tones are ignored). Punctuation and search syntax are ignored; the first 100 characters are searched.","name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":24},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","enum":["auto","text","semantic"],"default":"auto"},"required":false,"name":"mode","in":"query"},{"schema":{"type":"string","enum":["0","1"],"default":"1","description":"1 (default) excludes adult GIFs."},"required":false,"description":"1 (default) excludes adult GIFs.","name":"safe","in":"query"}],"responses":{"200":{"description":"A page of the query’s list","content":{"application/json":{"schema":{"type":"object","properties":{"build":{"type":"string","description":"Index build; a change between pages means re-fetch from offset 0."},"gen":{"type":"integer","description":"Cache generation."},"q":{"type":"string","description":"The query as searched (normalised)."},"semantic":{"type":"boolean"},"count":{"type":"integer","description":"Length of this query's list (at most 200), not the number of matching GIFs."},"offset":{"type":"integer"},"limit":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Gif"},"description":"Up to `limit` items from `offset`. Fewer (or none) past the end of the list: page until fewer than `limit` come back."}},"required":["build","gen","q","semantic","count","offset","limit","items"]}}}},"400":{"description":"Invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Client not registered, or adult content not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Upstream or registry unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/suggest":{"get":{"summary":"Autocomplete search terms","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":500},"required":true,"name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":8},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["0","1"],"default":"1","description":"1 (default) excludes adult GIFs."},"required":false,"description":"1 (default) excludes adult GIFs.","name":"safe","in":"query"}],"responses":{"200":{"description":"Terms","content":{"application/json":{"schema":{"type":"object","properties":{"build":{"type":"string","description":"Index build; a change between pages means re-fetch from offset 0."},"gen":{"type":"integer","description":"Cache generation."},"q":{"type":"string"},"terms":{"type":"array","items":{"type":"object","properties":{"term":{"type":"string"},"kind":{"type":"string","enum":["tag","character","title","text"]},"n":{"type":"integer"},"fuzzy":{"type":"boolean"}},"required":["term","kind","n"]}}},"required":["build","gen","q","terms"]}}}},"400":{"description":"Invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Client not registered, or adult content not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Upstream or registry unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/gifs/{id}/similar":{"get":{"summary":"GIFs similar to one GIF","parameters":[{"schema":{"type":"string","pattern":"^\\w[\\w.-]{0,199}$","example":"<sha256>.gif"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":24},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["0","1"],"default":"1","description":"1 (default) excludes adult GIFs."},"required":false,"description":"1 (default) excludes adult GIFs.","name":"safe","in":"query"}],"responses":{"200":{"description":"Nearest GIFs, nearest first","content":{"application/json":{"schema":{"type":"object","properties":{"build":{"type":"string","description":"Index build; a change between pages means re-fetch from offset 0."},"gen":{"type":"integer","description":"Cache generation."},"items":{"type":"array","items":{"$ref":"#/components/schemas/Gif"}}},"required":["build","gen","items"]}}}},"400":{"description":"Invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Client not registered, or adult content not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown GIF (cached like a 200)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Upstream or registry unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}