Public profile API
GET /public/u/{username}: everything needed to render taste.dev/@name, in one unauthenticated request.
This is the document taste.dev renders. It is public, needs no key, and validates against profile-1.json (Schemas).
GET https://api.taste.dev/public/u/{username}usernamestringpathrequiredThe handle, without the @. Case-insensitive: Mika and mika are the same page, and the response carries the canonical lowercase username.
Example
curl https://api.taste.dev/public/u/mikaconst res = await fetch(`https://api.taste.dev/public/u/${encodeURIComponent(name)}`);
const page = res.ok ? await res.json() : null; // 404: no builder hereimport requests
res = requests.get(f"https://api.taste.dev/public/u/{name}")
page = res.json() if res.ok else None # 404: no builder here{
"username": "mika",
"display_name": "Mika Tanaka",
"headline": "Shipping tiny AI apps weekly",
"photo_url": "https://api.taste.dev/avatars/glasses/mika.svg",
"building": "An AI receptionist for indie pharmacies",
"level": "shipping",
"tools": ["claude_code", "cursor", "v0"],
"topics": ["AI agents", "Health"],
"needs": ["Marketing / growth", "Feedback"],
"gives": ["Engineering", "AI / ML"],
"here_for": ["Find people to build my idea"],
"links": [
{ "title": "X / Twitter", "url": "https://x.com/mikaships" },
{ "title": "mika.build", "url": "https://mika.build" }
],
"projects": [
{
"id": "7b1c9e52-2f4a-4c1e-9a55-0d6f3c1e8a10",
"slug": "counter",
"name": "Counter",
"tagline": "The AI receptionist for indie pharmacies",
"followers": 214,
"url": "https://counter.mika.build",
"description": null,
"image_url": "https://storage.googleapis.com/taste-uploads/counter.png",
"icon": "💊",
"stars": 412
},
{
"id": "c0a4f1d8-6e3b-4b8a-8f0e-5b2d7a9c4e21",
"slug": "tidepool",
"name": "Tidepool",
"tagline": "Voice notes in, weekly reflections out",
"followers": 96,
"url": "https://github.com/mikatanaka/tidepool",
"description": null,
"image_url": null,
"icon": "🌊",
"stars": 1280
}
],
"badges": [
{ "key": "first_ship", "label": "First Ship", "category": "shipping", "tier": 1 }
],
"github_handle": "mikatanaka",
"readme_widgets": [
{
"kind": "skill_icons",
"service": "skillicons.dev",
"src": "https://skillicons.dev/icons?i=ts,react,swift",
"href": null,
"alt": "TypeScript, React, Swift",
"live": true
}
],
"joined": "2026-09",
"support_links": [
{ "platform": "buymeacoffee", "url": "https://buymeacoffee.com/mikaships" },
{ "platform": "github_sponsors", "url": "https://github.com/sponsors/mikatanaka" }
],
"theme": {
"schema": 1,
"id": "terminal",
"name": "Terminal",
"author": "taste",
"description": "Green phosphor on a dark tube. The original.",
"mode": "dark",
"colors": {
"background": "#020803",
"background2": "#06120B",
"surface": "#0B1510",
"line": "#1D2E24",
"text": "#EAF3EE",
"muted": "#8AA597",
"accent": "#AEF8D0",
"onAccent": "#04170E",
"highlight": "#39FF6A"
},
"background": { "type": "gradient", "colors": ["#06120B", "#020803"], "angle": 180 },
"effects": { "scanlines": true, "grain": 0, "glow": true },
"type": { "display": "sans", "body": "sans", "label": "pixel", "headingStyle": "prompt" },
"shape": { "radius": 18, "avatar": "circle", "border": 1 },
"layout": {
"order": ["building", "tools", "links", "projects", "pay_it_forward", "topics", "here_for", "badges"],
"links": "buttons",
"projects": "cards",
"tools": "tiles"
}
},
"card": { "schema": 1, "stats": ["streak", "stars", "projects", "joined"] },
"stats": [
{ "id": "streak", "label": "Streak", "value": 12 },
{ "id": "stars", "label": "Stars", "value": 1692 },
{ "id": "projects", "label": "Projects", "value": 2 },
{ "id": "joined", "label": "Joined", "value": "2026-09" }
]
}Response fields
usernamestringrequiredThe canonical handle, lowercase. The page lives at taste.dev/@{username}.
display_namestringrequiredThe builder's name.
headlinestring | nullrequiredOne line about them.
photo_urlstring | nullrequiredTheir photo (or pixel avatar). null when there is none: draw an initial, or a pixel avatar from /avatars/auto/{username}.svg.
buildingstring | nullrequiredWhat they're building.
levelstring | nullrequiredHow often they ship, e.g. "shipping".
toolsstring[]requiredTool slugs, e.g. claude_code, cursor.
topicsstring[]requiredWhat they're into.
givesstring[]requiredCan give (pay it forward).
needsstring[]requiredLooking for.
here_forstring[]requiredWhy they're on Taste.
linksobject[]requiredUp to 12 {title, url} in the builder's order. URLs are http(s).
projectsobject[]required{id, slug, name, tagline, followers, url, description, image_url, icon, stars}. image_url is a screenshot or an http(s) icon; icon is an emoji or image URL; stars is GitHub stars (≥ 0). description is one line about it (the repo description for projects imported from GitHub), or null.
Each project is a product: slug names its page at taste.dev/@{username}/{slug} (Products), tagline is its one-line pitch (or null) and followers counts who follows it in the app. id, slug, tagline and followers are newer: older responses omit them, and a project without a slug has no product page yet, so link to its url instead.
badgesobject[]required{key, label, category, tier} in the order earned. tier starts at 1.
github_handlestring | nullrequiredTheir GitHub username.
readme_widgetsobject[]Images the builder brought over from their GitHub profile README with Import from GitHub — stats cards, streaks, banners, badge walls, typing SVGs — in order, at most 20. Each is {kind, service, src, href, alt, live}: kind names what it is (stats_card, top_languages, streak, trophies, activity_graph, snake, banner, typing, skill_icons, badge, counter_badge, wakatime, tokscale, trendshift, gif, image, … — new kinds may appear), src is always https, href is where it links (or null), live means the service draws it from live data. Optional: older responses omit it.
Render each as a plain image — never as HTML — lazily, with no referrer, linked to href, and hide any image that fails to load: these are third-party services and some go down.
support_linksobject[]Where to back the builder, in their order: {platform, url}. platform is one of buymeacoffee, kofi, github_sponsors, polar, patreon, stripe or other (new platforms may appear: treat one you don't know as other); url is https. taste.dev shows them as a Support section of brand-colored buttons, and a product page's Support button falls back to the first one. Optional: older responses omit it.
joinedstringrequiredJoin month, "YYYY-MM".
themeobjectrequiredThe complete, resolved theme (Themes). If the stored theme no longer exists, this is terminal.
cardobjectrequired{"schema": 1, "stats": [ids]}: which stats show, in order (Card stats).
statsobject[]requiredThe card's 2–4 stats as {id, label, value}, in card order. value is an integer, or "YYYY-MM" for joined.
Hidden sections
Builders can hide parts of their profile. A hidden section still appears in the response, blank (null or []), so the shape never changes:
| Hidden in the app | Blank in the response |
|---|---|
| Story | building, level, gives |
| Portfolio | projects |
| Stack | tools |
| Interests | topics, here_for |
| Links | links, github_handle, readme_widgets |
stats always show: they're the builder's pick.
Not found
Anything that isn't a live, public page returns the same thing:
{ "error": "not_found" }That covers an unknown or malformed handle, an account that is deleted, paused or suspended, a profile held for moderation, and a member who hasn't finished a profile. Treat every 404 as "no builder here". taste.dev shows a "claim this name" page.
Caching
The response is sent with:
Cache-Control: public, max-age=60, stale-while-revalidate=600Edits show up within about a minute. taste.dev re-renders pages on the same one-minute cycle. The catalog and schema endpoints are cached for a day (public, max-age=86400).
Privacy
The page is public by design, so it carries only what the builder put on their page. It never includes:
- their account id, email address or phone number
- their location, timezone or calendar
- verification details
- who they follow, connect or talk with, and their posts
- anything in a section they hid
Stats are counts, not lists: followers is a number, never who.