{"openapi":"3.1.0","info":{"title":"thetattoo.world API","version":"1.0.0","description":"Public read API for the thetattoo.world tattoo artist directory. Authentication: none. Do not send an API key. Each server instance applies a 60-second sliding window per client IP and route group; the numbers are stated on each operation. Portfolio images are not licensed for reuse. Structured metadata is CC BY 4.0."},"servers":[{"url":"https://thetattoo.world","description":"Production"}],"externalDocs":{"description":"thetattoo.world API documentation","url":"https://thetattoo.world/docs"},"tags":[{"name":"Artists","description":"Public artist records shown in the directory."},{"name":"Map","description":"Aggregated locations for the public map."}],"paths":{"/api/artists/compact":{"get":{"operationId":"listArtists","tags":["Artists"],"summary":"List public artists","description":"Return every artist currently shown on thetattoo.world as a JSON array. Use this to search or filter by name, city, country, or style on the client. Rate limit: 90 requests per IP per 60 seconds on each server instance. The response is cacheable for one hour.","responses":{"200":{"description":"Public artist records.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicArtist"}}}}},"429":{"description":"Too many requests from this IP for this route group.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","const":60}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/artists/{id}":{"get":{"operationId":"getArtistById","tags":["Artists"],"summary":"Get one public artist","description":"Return the full public record for one artist id. Rate limit: 180 requests per IP per 60 seconds on each server instance. Unknown, hidden, or invalid ids return 404 with a JSON null body.","parameters":[{"name":"id","in":"path","required":true,"description":"Artist id, the lowercased Instagram handle. 1 to 30 characters: a-z, digits, dot, underscore.","schema":{"type":"string","pattern":"^[a-z0-9._]{1,30}$"}}],"responses":{"200":{"description":"The public artist record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artist"}}}},"404":{"description":"No public artist with that id.","content":{"application/json":{"schema":{"type":"null"}}}},"429":{"description":"Too many requests from this IP for this route group.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","const":60}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/map-data":{"get":{"operationId":"getMapPoints","tags":["Map"],"summary":"List map points","description":"Return aggregated latitude and longitude points for the public map. Pass style to count artists of one style per city. Omit style to return one point per city and style. Rate limit: 120 requests per IP per 60 seconds on each server instance.","parameters":[{"name":"style","in":"query","required":false,"description":"Style tag. When set, points are limited to artists who list this style.","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Map points and the style vocabulary seen in the catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapData"}}}},"429":{"description":"Too many requests from this IP for this route group.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","const":60}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/map-hex-artists":{"post":{"operationId":"listArtistsAtLocations","tags":["Map"],"summary":"List artists at locations","description":"Return id and name for public artists whose city and country match a posted location. Optional style keeps only artists tagged with that style. Rate limit: 60 requests per IP per 60 seconds on each server instance. An empty locations array returns an empty artists array.","requestBody":{"required":true,"description":"Locations to match, and an optional style filter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapHexRequest"}}}},"responses":{"200":{"description":"Matching artists, sorted by name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapHexResponse"}}}},"400":{"description":"The body was not valid JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests from this IP for this route group.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","const":60}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"PublicArtist":{"type":"object","required":["id","name","location","styles","links","tattoo_count"],"properties":{"id":{"type":"string","description":"Lowercased Instagram handle.","pattern":"^[a-z0-9._]{1,30}$"},"name":{"type":"string","description":"Display name."},"location":{"type":"object","required":["city","country"],"properties":{"city":{"type":"string","description":"City where the artist works. May be empty."},"country":{"type":"string","description":"Country where the artist works."}}},"styles":{"type":"array","items":{"type":"string"},"description":"Style tags from the catalogue vocabulary."},"links":{"type":"object","required":["instagram"],"properties":{"instagram":{"type":"string","description":"Public Instagram profile URL."}}},"tattoo_count":{"type":"integer","minimum":0},"thumb":{"type":"string","description":"Representative thumbnail URL."},"thumbs":{"type":"array","items":{"type":"string"}},"bio":{"type":"string","description":"Public biography excerpt."},"updated_at":{"type":"string","description":"Catalogue update date, YYYY-MM-DD."},"follower_count":{"type":"integer","minimum":0},"instagram_profile_pic":{"type":"string"},"verified":{"type":"string","enum":["artist","studio","need_to_check"]},"instagram_enriched":{"type":"boolean"},"followed_by_artists_count":{"type":"integer","minimum":0}}},"Artist":{"allOf":[{"$ref":"#/components/schemas/PublicArtist"},{"type":"object","required":["tattoos","updated_at"],"properties":{"links":{"type":"object","properties":{"instagram":{"type":"string"},"website":{"type":"string"}}},"tattoos":{"type":"array","items":{"$ref":"#/components/schemas/TattooImage"}},"updated_at":{"type":"string"}}}]},"TattooImage":{"type":"object","required":["image_id","image_full","image_thumb"],"properties":{"image_id":{"type":"string"},"image_full":{"type":"string","description":"Full-size image URL."},"image_thumb":{"type":"string","description":"Thumbnail image URL."}}},"MapPoint":{"type":"object","required":["lat","lng","count","city","country","label"],"properties":{"lat":{"type":"number","description":"Latitude in degrees."},"lng":{"type":"number","description":"Longitude in degrees."},"count":{"type":"integer","minimum":1},"city":{"type":"string"},"country":{"type":"string"},"label":{"type":"string"},"style":{"type":"string","description":"Present when points are split by style."}}},"MapData":{"type":"object","required":["points","styles","totalArtists"],"properties":{"points":{"type":"array","items":{"$ref":"#/components/schemas/MapPoint"}},"styles":{"type":"array","items":{"type":"string"}},"totalArtists":{"type":"integer","minimum":0}}},"MapLocation":{"type":"object","required":["country"],"properties":{"city":{"type":"string"},"country":{"type":"string"}}},"MapHexRequest":{"type":"object","required":["locations"],"properties":{"locations":{"type":"array","items":{"$ref":"#/components/schemas/MapLocation"}},"style":{"type":["string","null"],"description":"When set, only artists tagged with this style are returned."}}},"ArtistName":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"MapHexResponse":{"type":"object","required":["artists"],"properties":{"artists":{"type":"array","items":{"$ref":"#/components/schemas/ArtistName"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}}}}}