Skip to main content
Auric Artisan · Documentation

The Shade Library API

Date: August 29, 2026 For: Developers Category: Guide Author: Chirag Bansal
Back to Documentation API reference Open the library

Overview

A shade scale is not a palette. A palette is a set of colours that look good together; a scale is one colour taken through lightness so that some steps are surfaces and other steps are text on them. That is a different job, and it has different failure modes — which is why the measurements here are not the ones the palette or harmony collections use.

One number before you start. All 8,192 scales in this collection run one way, and all of them can carry body text — but only 8.2% have genuinely even steps. Monotonicity and contrast are table stakes that the generator always gets right. Evenness is what actually separates a usable token ramp from one that feels arbitrary in use, and it is the filter to reach for.

Table of contents

  1. 1. What this API is for
  2. 2. Getting a key and making your first call
  3. 3. The three things a scale has to get right
  4. 4. Browsing and paging
  5. 5. One scale, whole
  6. 6. The collection’s own numbers
  7. 7. Measuring a scale you already ship
  8. 8. The three ways a scale fails
  9. 9. Three complete tasks
  10. 10. What each call costs
  11. 11. Errors, limits and pitfalls
  12. 12. Endpoint reference

1. What this API is for

+

POST /v1/shade/scale generates a ramp from a base colour. These four are about ramps that already exist — ours, or yours.

  • Show me scales that are actually even — /v1/shade/library
  • Give me one whole, with every measurement — /v1/shade/library/{id}
  • What is in the collection, counted? — /v1/shade/library/stats
  • Does my token ramp work? — /v1/shade/evaluate

The last one is the reason the other three are worth having. 8,192 measured scales are the reference; the interesting question is whether the scale in your design system passes the same tests.

2. Getting a key and making your first call

+
curl -H "x-api-key: $AURIC_API_KEY" \
  "https://api.auricartisan.com/v1/shade/library?evenness=even&limit=3"

3. The three things a scale has to get right

+

It has to run one way

monotonic. If lightness rises and falls, two different steps read as the same tone and one of them is doing no work. worst_reversal says how far the biggest backwards move went, in OKLab lightness.

Its steps have to be evenly spaced

step_ratio is the largest step over the average one, measured as OKLab distance between consecutive members. A ratio, not an absolute, so it does not move with the number of steps — a 5-step and a 21-step scale are directly comparable. Under 1.6 is even, under 2.6 is slightly-uneven, above that is lumpy.

This is the same shape of measure /v1/gradient/analyse uses for banding, and for the same reason: a ramp that lurches somewhere reads as a step, whether it is painted as a gradient or shipped as tokens.

You have to be able to set text on it

usable_span is the contrast between the lightest and darkest steps; text_pairs counts how many step pairs clear AA and AAA. A scale can be perfectly monotonic and perfectly even and still be four shades of off-white — a surface ramp, not a text scale.

4. Browsing and paging

+
GET /v1/shade/library?evenness=even&sort=span&order=desc&limit=2
{
  "collection": { "total": 8192, "methods": 10 },
  "matched": 670,
  "share_of_collection": 8.18,
  "next_offset": 2,
  "scales": [
    {
      "id": 1064,
      "shade_id": "shade_0000tk",
      "name": "Ink Paper #006B4C #01065",
      "method": "ink-paper",
      "base": "#006B4C",
      "steps": [
        { "token": "100", "hex": "#FCFDFC" },
        { "token": "300", "hex": "#A3AAA6" },
        { "token": "500", "hex": "#006B4C" },
        { "token": "700", "hex": "#232826" },
        { "token": "900", "hex": "#010101" }
      ],
      "step_count": 5,
      "evenness": "even",
      "monotonic": true,
      "usable_span": 20.47,
      "carries_body_text": true
    }
  ]
}

Every step carries the token it answers to — 100, 300, 500 — not just a hex, so a scale can be dropped into a token file without re-deciding what to call each rung.

Filters

  • evenness — even, slightly-uneven or lumpy. The one that separates the collection.
  • method — one of ten, e.g. tailwind-like, oklch-ramp, ink-paper.
  • steps — exactly this many rungs, 2–64.
  • hue — the base colour’s band, red through pink.
  • min_span — contrast between the extremes.
  • min_score — the generator’s own quality floor.
  • monotonic, carries_body_text — both true for every record, so useful mainly as assertions.
  • sort — id, score, steps, span or evenness. Ascending evenness means evenest first, which is what anyone asking for it means.

5. One scale, whole

+
GET /v1/shade/library/shade_000000

Adds the full measurement block: mean_step, max_step, lightness_range, worst_reversal and the AA/AAA pair counts.

Shade ids are base 36. shade_00000a is row 10 and shade_000012 is row 38, not 12. Both spellings work — /v1/shade/library/12 and /v1/shade/library/shade_00000c name the same scale — but reading the digits as decimal will quietly hand you the wrong record for every row past nine.

6. The collection’s own numbers

+
GET /v1/shade/library/stats
{
  "collection": { "total": 8192 },
  "mean_score": 0.631,
  "mean_step_ratio": 2.19,
  "mean_usable_span": 18.87,
  "monotonic":         { "count": 8192, "share": 100 },
  "carries_body_text": { "count": 8192, "share": 100 },
  "evenness": [
    { "name": "slightly-uneven", "count": 6380, "share": 77.88 },
    { "name": "lumpy",           "count": 1142, "share": 13.94 },
    { "name": "even",            "count":  670, "share":  8.18 }
  ],
  "method": [ ...10 entries... ],
  "step_count": [ ...8 entries... ]
}

Two of these are 100%, and that is worth stating plainly rather than burying: monotonicity and text-carrying are not useful filters here, because the generator never gets them wrong. If monotonic.share ever drops below 100, something in the generator has regressed.

Evenness is the discriminating measure. The mean step ratio is 2.19 — the average scale’s biggest jump is more than twice its average one — and fewer than one in twelve comes in under 1.6.

7. Measuring a scale you already ship

+
POST /v1/shade/evaluate
Content-Type: application/json

{ "steps": ["#FFFFFF", "#E0E0E0", "#A0A0A0", "#606060", "#202020"] }
{
  "verdict": "a clean scale",
  "monotonic": true,
  "worst_reversal": 0,
  "step_ratio": 1.298,
  "evenness": "even",
  "usable_span": 16.29,
  "text_pairs": { "aa": 3, "aaa": 2, "total": 10 },
  "carries_body_text": true,
  "notes": [
    "5 of 10 step pairs clear AA, so you can build surface-and-text
     combinations inside the scale itself."
  ]
}

Order matters, because monotonicity is about order — send the steps as your scale declares them. Either direction is fine: light-to-dark and dark-to-light both count as monotonic, and the verdict is identical for a scale and its reverse.

8. The three ways a scale fails

+

Each has a different fix, so the response names which one happened rather than returning a single score.

It turns round

["#FFFFFF", "#202020", "#E0E0E0", "#606060"]
→ "not a usable ramp"  ·  monotonic: false  ·  worst_reversal: 0.6632

Reorder it. Two of those steps currently read as the same tone.

It is lumpy

["#FFFFFF", "#FEFEFE", "#FDFDFD", "#101010"]
→ "a lumpy scale"  ·  monotonic: true  ·  step_ratio: 2.978

It runs the right way — that is not the problem. Three of those steps are indistinguishable and the fourth is a cliff. Redistribute the rungs; an OKLCH ramp is the usual fix.

It is a surface ramp, not a text scale

["#FFFFFF", "#F6F6F6", "#EDEDED", "#E4E4E4"]
→ "a surface ramp, not a text scale"  ·  evenness: even  ·  usable_span: 1.27

Perfectly monotonic, perfectly even, and useless for setting type on itself. A verdict that only looked at evenness would call this the best scale in the world. Extend the range, or accept that the text colour comes from outside it.

9. Three complete tasks

+

Audit the ramps in your design system

import tokens from "./tokens.json" with { type: "json" };

for (const [name, scale] of Object.entries(tokens.color)) {
  const r = await (await fetch("https://api.auricartisan.com/v1/shade/evaluate", {
    method: "POST",
    headers: { "content-type": "application/json", "x-api-key": KEY },
    body: JSON.stringify({ steps: Object.values(scale) }),
  })).json();

  if (r.verdict !== "a clean scale") {
    console.warn(`${name}: ${r.verdict}`);
    r.notes.forEach((n) => console.warn("  " + n));
  }
}

Find a replacement for one that failed

GET /v1/shade/library?evenness=even&steps=11&hue=blue&sort=span&order=desc&limit=6

Eleven rungs, cleanly spaced, blue, widest range first. Note that steps=11 is worth pinning: 36.5% of the collection is eleven-step, which is what most token systems expect.

Check a generated scale before you ship it

const made = await post("/v1/shade/scale", { base: "#2E6FF2", steps: 11 });
const checked = await post("/v1/shade/evaluate", { steps: made.steps.map((s) => s.hex) });
if (checked.evenness === "lumpy") { /* try another method */ }

Generating and checking are different endpoints on purpose: the generator optimises for its method, and this tells you what the result actually measures.

10. What each call costs

+
  • GET /v1/shade/library/{id} — 1 credit.
  • GET /v1/shade/library — 3 credits.
  • POST /v1/shade/evaluate — 3 credits.
  • GET /v1/shade/library/stats — 10 credits. Walks all 8,192 and every pairwise contrast in each.

API credits are a separate balance from tool tokens; neither pays for the other. Statistics do not change between calls — cache them.

11. Errors, limits and pitfalls

+
  • Shade ids are base 36. See section 5. This is the one that will catch you.
  • Send steps in scale order. Monotonicity is a statement about order; a shuffled scale is correctly reported as broken.
  • 3 to 64 steps on evaluate. Two colours are not a ramp — there is only one gap, so evenness is meaningless.
  • Do not filter on monotonic or carries_body_text expecting them to narrow anything. Both are 100% in this collection.
  • step_ratio is a shape measure, not a magnitude. A scale with tiny even steps and one with large even steps both score near 1.

12. Endpoint reference

+
EndpointMethodCreditsAnswers
/v1/shade/libraryGET3Which scales match this filter, and how much of the collection is that?
/v1/shade/library/{id}GET1One scale, with every measurement.
/v1/shade/library/statsGET10What the collection contains, counted rather than asserted.
/v1/shade/evaluatePOST3Does this ramp run one way, step evenly, and carry text?

The machine-readable form is in the API reference, generated from the same catalogue the server routes from.

To see it working without writing any code, open the shade library — the evenness filter and the measurements on every card are the same ones this API exposes.