Get startedOverview

Customize your Taste page

Your page at taste.dev/@you is data: a theme, a stats card, your links and your handle. Here is what you can change and the API behind it.

Every builder on Taste gets a page at taste.dev/@name. The app and the website draw it from the same JSON, so what you pick in the app is exactly what the web shows. This documentation covers what's customizable, the formats, and the public API any app can read.

What you can customize

WhatHowWhere it shows
ThemePick one of the built-ins: terminal (default), studio, pearlColors, background, fonts, card shape and section layout of your whole page
Card statsChoose 2–4 stats, in orderThe stat row under your name
LinksUp to 12 ordered linksYour link-in-bio buttons
HandleClaim a usernameYour URL: taste.dev/@name
AvatarUpload a photo, or wear a generated pixel avatarThe top of your page and your share card

The model

A page is three documents that travel together:

  • Profile: the data. Name, headline, what you're building, tools, links, projects, badges.
  • Theme: how it looks. Nine colors, a background, font roles, shape and section layout. Data only: a theme carries no CSS and no code. See Themes.
  • Card: which stats show, in order. See Card stats.

GET /public/u/{username} returns all three in one response: the profile fields, a fully resolved theme, the card, and the chosen stats with their values. Each document has a JSON Schema with an integer version (profile-1, theme-1, card-1). See Schemas and Versioning.

Quick start

Read any public page. No key, no auth:

cURLbash
curl https://api.taste.dev/public/u/mika
JavaScriptjs
const res = await fetch('https://api.taste.dev/public/u/mika');
if (res.status === 404) throw new Error('No builder here');
const page = await res.json();
console.log(page.display_name, page.theme.id, page.stats);
Pythonpython
import requests

page = requests.get("https://api.taste.dev/public/u/mika").json()
print(page["display_name"], page["theme"]["id"], page["stats"])

Then render it:

  1. Paint theme.colors.background (or the theme.background gradient) and set fonts from theme.type.
  2. Show stats as the stat row, in order: label and value. Skip ids you don't recognize.
  3. Lay out the sections in theme.layout.order, each in its variant (layout.links, layout.projects, layout.tools).

Endpoints

The API host is https://api.taste.dev.

MethodPathAuthPurpose
GET/public/u/{username}noneA public page: profile, theme, card and stats
GET/profile/stats/catalognoneEvery stat a card can show
GET/schemasnoneIndex of the JSON Schemas
GET/schemas/{file}noneOne schema, e.g. /schemas/theme-1.json
GET/avatars/stylesnoneThe pixel-avatar styles
GET/avatars/{style}/{seed}.svgnoneA generated pixel avatar
GET/themesyesThe built-in themes, full JSON, in picker order
PUT/profile/themeyesPick your theme
PUT/profile/cardyesPick your card's stats
PUT/profile/linksyesReplace your links
PUT/profile/usernameyesClaim or change your handle
GET/username/available?u={name}yesIs a handle free for you?

Authenticated endpoints take Authorization: Bearer <access token>: the signed-in member's session from the Taste app. There is no third-party app registration yet, so today these are what the app calls on your behalf.

PUT /profile/links replaces your whole list, in order, and returns what was saved.

http
PUT /profile/links
Content-Type: application/json

{"links": [{"title": "Site", "url": "mika.build"}, {"url": "https://github.com/mikatanaka"}]}
Responsejson
{
  "links": [
    { "title": "Site", "url": "https://mika.build" },
    { "title": "github.com", "url": "https://github.com/mikatanaka" }
  ]
}
  • At most 12 links.
  • URLs must be http or https. A bare domain gets https://. Other schemes (mailto:, ftp://) are rejected.
  • title is optional: it defaults to the URL's host, is trimmed, and is at most 60 characters.
  • Anything invalid returns 400 {"error": "invalid"}.

Handle

Your handle is your URL: taste.dev/@name. Handles are 1–30 characters of lowercase letters, digits and _, with single . or - allowed between other characters. They start with a letter or digit and are case-insensitive (/@Mika redirects to /@mika).

http
GET /username/available?u=mika.builds
Responsejson
{ "available": false, "reason": "taken" }

reason is taken, invalid, reserved, premium (with a price_usd), or null when it's free. Your own current handle reads as available to you.

http
PUT /profile/username
Content-Type: application/json

{"username": "mika.builds"}

→ 200 {"username": "mika.builds"}

StatusBodyWhen
400{"error": "invalid", "reason": "invalid"}Malformed
400{"error": "invalid", "reason": "reserved"}Reserved (brands, taste.dev routes, tool names)
402{"error": "premium", "reason": "premium", "price_usd": 50}1–5 character names are premium, not purchasable yet
409{"error": "conflict", "reason": "taken"}Someone has it
429{"error": "rate_limited"}You renamed in the last 30 days (one rename a month)

Pixel avatars

Members without a photo wear a generated pixel avatar. They are deterministic: the same style and seed always draw the same SVG, and responses are cached for a year.

bash
curl https://api.taste.dev/avatars/styles
curl https://api.taste.dev/avatars/glasses/mika.svg

The styles are hoodie, visor, headset, glasses, robot, cat, astro, wizard, rain, ghost, alien and pixel. auto picks one from the seed. An unknown style draws pixel. The seed is 1–64 characters of A-Z a-z 0-9 . _ -.