HTTP API · shapeaddress.com/v1

The same picture over HTTP

For a page with no build step, a language with no port yet, or a quick look. Every response is the output of the same core library, cached as immutable for a year.

Two addresses, straight from the API:

<img src="https://shapeaddress.com/v1/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.svg?size=64" width="64" alt="">
<img src="https://shapeaddress.com/v1/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.svg?layout=coin&size=64" width="64" height="64" alt="">
…6045.svg?size=64
…6045.svg?layout=coin&size=64

Routes

RouteContent typeBody
GET /v1/{address}.svgimage/svg+xmlThe SVG, exactly shapeaddress(address, options). Works in <img>, CSS url() and as a file.
GET /v1/{address}.jsonapplication/jsonThe cells as data, exactly shapeaddress.cells(address, options): dictionary, layout, hex, one entry per cell with byte, indices, names and colour hex, plus the checksum cell.
GET /v1/{address}.txttext/plainThe spoken legend on one line, cells separated by a comma and a space: purple flower, purple three-dots, …
GET /v1/dictionariesapplication/jsonWhat this server can draw: [{ id, name, colors, glyphs, license }].

{address} is hex, with or without 0x, any case, even length. It need not be 20 bytes. Anything that is not hex is a 400.

Query parameters

The names are the core options, with bg for background. Omitted parameters take the core defaults, so a bare URL gives the same picture as a bare shapeaddress(address) call.

ParameterValuesDefault
layoutgrid, coingrid
size16 to 1024, clamped into that range64
styletile, glyphtile
cellrounded, circle, squarerounded
gap0 to 408
radius0 to 5014
bga CSS colour, URL-encoded (%23ffffff for white)transparent
palettedefault, cvddefault
dictionaryan id from /v1/dictionariessa330

labels (1 or true) and title (up to 200 characters) are accepted; highlight is not served, it is presentation for a page that already has the library. Any other parameter, or a value outside its set, is a 400.

https://shapeaddress.com/v1/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.svg?layout=coin&size=128&style=glyph&bg=%23ffffff
https://shapeaddress.com/v1/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.json?dictionary=fa330
https://shapeaddress.com/v1/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045.txt

Headers and caching

A ShapeAddress of a given address and options never changes, so the response says so.

HeaderValueWhy
Cache-Controlpublic, max-age=31536000, immutableBrowsers and CDNs keep it for a year and never revalidate.
ETaghash of the bodyIf-None-Match gets a 304.
Access-Control-Allow-Origin*Fetch it from any page.
X-Content-Type-OptionsnosniffSVG is served as an image, never sniffed as a document.
Set-CookieneverNo session, no tracking.

A 400 response is cached for an hour, so a bad URL does not become a load on the server. There is no PNG route: the server keeps zero dependencies, and every browser renders SVG in <img>. For raster output, draw the SVG onto a canvas on the client.

Privacy: wallets should bundle the library

Every request to this API tells a server which address someone looked at and when. For a wallet, an explorer or anything that shows a user's own accounts, that is a leak. The library is one file, 5 kB gzipped, with no dependencies. Bundle it and render locally. Use the API for public pages, prototypes and languages with no port.

Self-host

The server is the @shapeaddress/api package: a Node http handler with no dependencies. Run it anywhere Node 22 runs.

# one command, listens on $PORT
npx @shapeaddress/api

# as a module, in your own server (Node http, or any framework that gives you req and res)
const { createHandler, toNode } = require('@shapeaddress/api');
require('node:http').createServer(toNode(createHandler())).listen(8787);
# Docker, from the repository
git clone https://github.com/ShapeAddress/shapeaddress.git
cd shapeaddress
docker build -t shapeaddress-api .
docker run --rm -p 8787:8787 shapeaddress-api

curl -s http://localhost:8787/v1/dictionaries

A self-hosted server registers the dictionaries it starts with, so a private dictionary stays private. Put a CDN in front of it; the cache headers do the rest.