QTY

Qty API

Qty parses and converts quantities in code. It does not call a model. It does not invent numbers. Temperature is affine. US and imperial gallons differ. The international foot is the default foot. Pace is not speed unless a speed unit is requested. Every JSON response has a top-level see object pointing at docs.

Version 1.0.0. This page is rendered from the same document as /openapi.json.

GET /health

health

Returns ok. No payment, no Redis, no network.

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "ok": true,
  "service": "qty",
  "version": "1.0.0"
}

GET /units

listUnits

Call this before convert when you are unsure of a code. Free.

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "units": [
    {
      "code": "mi",
      "aliases": [
        "mile",
        "statute mile"
      ],
      "dimension": "length",
      "definitionId": "length.mile.statute"
    }
  ]
}

POST /v1/convert

convert

Use when you already have a number and two codes from GET /units, such as 10 mi to km or 32 F to C. Temperature is affine. If the text is messy (5'11", a stick of butter, a price, a size, a date), this returns 400 use_parse and you must call POST /v1/parse. Deterministic. assumptions is always an array. Limited to 60 requests per 60 seconds per client. Over the limit returns 429 rate_limited with Retry-After. The hint points at paid POST /v1/batch ($0.01). The client is ipAddress() from @vercel/functions (x-real-ip), otherwise the first public address in x-vercel-forwarded-for, then x-forwarded-for. Ports and non-addresses are ignored. The address is not stored. Responses include x-ratelimit-source (redis, memory, or none), x-ratelimit-key-prefix (first 8 hex chars of the client hash), and x-ratelimit-window (the fixed window id). The counter is shared through the same Redis as the feed (KV_REST_API_* or UPSTASH_REDIS_REST_*). A Redis error does not fail the route.

Example request

{
  "value": 10,
  "from": "mi",
  "to": "km"
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "result": 16.09344,
  "from": "mi",
  "to": "km",
  "formula": "10 mi × 1.609344 km/mi = 16.09344 km",
  "definitionId": "length.mile.statute",
  "assumptions": []
}

GET /v1/feed

feed

Free. Cached for about 2 seconds. Events contain route, kind, from value and unit, to value and unit, or a short category, plus a timestamp. Raw text, receipts, wallets, IPs, and headers are never stored. Empty when Redis is not configured.

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "events": [],
  "totals": {
    "all": 0,
    "routes": {}
  },
  "configured": false
}

GET /v1/parse

parseQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/parse ($0.002). The handler does not run.

POST /v1/parse

parse

Interpret messy quantity text into a list of quantities. Call this when convert returns use_parse. Deterministic. Never invents a number: unknown amounts are null with a reason. Same text always returns the same quantities. Does not call a model or any other service. Price $0.002 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "text": "5'11\"",
  "to": "cm"
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "quantities": [
    {
      "value": 180.34,
      "unit": "cm",
      "siValue": 1.8034,
      "siUnit": "m",
      "formula": "5 ft × 0.3048 m + 11 in × 0.0254 m = 1.8034 m; 1.8034 m = 180.34 cm",
      "definitionId": "length.foot.international",
      "confidence": 0.98,
      "warnings": [],
      "assumptions": [
        "Feet and inches use the international foot (0.3048 m) and the international inch (0.0254 m)."
      ],
      "reason": null,
      "label": "height"
    }
  ]
}

GET /v1/batch

batchQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/batch ($0.01). The handler does not run.

POST /v1/batch

batch

Parse up to 100 messy texts for a flat price. One bad row does not fail the batch. Each row is ok with quantities or ok:false with a stable error code. Deterministic. No per-row upto pricing in v1. Price $0.01 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "items": [
    {
      "text": "10 mi",
      "to": "km"
    },
    {
      "text": "not a quantity"
    }
  ]
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "results": [
    {
      "index": 0,
      "ok": true,
      "quantities": []
    },
    {
      "index": 1,
      "ok": false,
      "error": {
        "code": "unparsed"
      }
    }
  ]
}

GET /v1/receipt

receiptQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/receipt ($0.001). The handler does not run.

POST /v1/receipt

receipt

Canonical audit JSON plus sha256 of a previous Qty result. Includes input, definition ids, formulas, and a timestamp. The hash ignores the timestamp so the same payload always hashes the same. No chain write. Price $0.001 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "quantities": [
    {
      "value": 180.34,
      "unit": "cm",
      "formula": "5 ft + 11 in",
      "definitionId": "length.foot.international"
    }
  ]
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "document": {
    "kind": "qty.receipt.v1",
    "definitions": [],
    "formulas": [],
    "timestamp": "2026-10-05T00:00:00.000Z"
  },
  "sha256": "0000000000000000000000000000000000000000000000000000000000000000"
}

GET /v1/compare-prices

compare-pricesQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/compare-prices ($0.002). The handler does not run.

POST /v1/compare-prices

compare-prices

Normalise unit prices and say which offer is cheaper. Currency is a label only; Qty never converts between currencies. Multipacks are totalled first. Returns price per base unit (litre, kilogram, or each). Price $0.002 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "offers": [
    "£2.50 for 750ml",
    "£3 for 1L"
  ]
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "comparison": {
    "cheaperIndex": 1,
    "currency": "GBP",
    "baseUnit": "L"
  }
}

GET /v1/scale-recipe

scale-recipeQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/scale-recipe ($0.002). The handler does not run.

POST /v1/scale-recipe

scale-recipe

Scale ingredient amounts by servings or a factor. Cup, tbsp, tsp, and stick convert between us, imperial, and si using the labelled assumption table. Mass and volume convert only for flour, sugar, butter, water, milk, and rice. Anything else is null with a reason. Price $0.002 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "fromServings": 4,
  "toServings": 6,
  "ingredients": [
    "2 cups flour",
    "1 stick of butter"
  ]
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "recipe": {
    "factor": 1.5,
    "definitionId": "recipe.scale.servings.v1"
  }
}

GET /v1/size

sizeQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/size ($0.002). The handler does not run.

POST /v1/size

size

Convert shoe and clothing sizes using Qty size table v1. Men's UK 9 shoe is EU 43 and US 10. Ambiguous gender or category returns null and a reason. Brand variance is always a warning. No interpolation. Price $0.002 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "text": "UK 9 men's shoe",
  "to": "EU"
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "quantities": [
    {
      "value": 43,
      "unit": "EU",
      "definitionId": "size.shoe.men.v1"
    }
  ]
}

GET /v1/date

dateQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/date ($0.002). The handler does not run.

POST /v1/date

date

Parse ISO-8601 durations, add N days from an explicit date, and convert IANA time zones. Working days skip Saturday and Sunday plus an optional caller holiday list. Daylight-saving gaps and overlaps are reported. There is no built-in holiday calendar. If the text needs a date and none was given, the value is null with a reason. Qty never uses the current clock. Price $0.002 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "text": "2026-01-15T12:00 America/New_York to Europe/London"
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "quantities": [
    {
      "textValue": "2026-01-15T17:00:00+00:00",
      "unit": "Europe/London",
      "definitionId": "date.timezone.v1"
    }
  ]
}

GET /v1/estimate

estimateQuote

GET returns HTTP 402 with the x402 price quote for POST /v1/estimate ($0.003). The handler does not run.

POST /v1/estimate

estimate

Estimate paint, tiles, concrete, or gravel. Paint uses 10 m² per litre per coat and defaults to 2 coats. Tiles default to 0.30 m × 0.30 m. A 25 kg concrete bag yields 0.012 m³. Gravel uses 1600 kg/m³. Waste defaults to 10 percent and is stated on the result. Assumptions are labels, not a quote from a supplier. Price $0.003 exact USDC on eip155:8453 (0x41F7c3FfC3Cda926797Eb388bB65Ccd8A1deC352) and solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (GhBZyUbBH2qdTbUXtNjFnbiqeh9c7rsReVU6kjJVCbp6). Unpaid POST and GET calls return 402 and do not include the result. Settlement happens only after a 2xx.

Example request

{
  "kind": "paint",
  "area": 40,
  "unit": "m2",
  "coats": 2,
  "wastePercent": 10
}

Example response

{
  "see": {
    "llms": "/llms.txt",
    "llmsFull": "/llms-full.txt",
    "openapi": "/openapi.json",
    "docs": "/docs",
    "x402": "/.well-known/x402.json",
    "agentCard": "/.well-known/agent-card.json",
    "mcp": "/mcp",
    "tools": "/tools/openai.json",
    "anthropicTools": "/tools/anthropic.json",
    "aiSdk": "/tools/ai-sdk.ts",
    "credits": "/docs/prepaid-credits"
  },
  "quantities": [
    {
      "value": 8.8,
      "unit": "L",
      "definitionId": "building.paint.v1",
      "assumptions": [
        "Paint coverage is 10 m² per litre per coat (Qty building assumption table v1)."
      ]
    }
  ]
}

GET /tools/openai.json

openaiTools

Function tools for convert, units, and every paid route. Free.

GET /tools/anthropic.json

anthropicTools

input_schema objects matching the MCP tool inputs. Free.

GET /tools/ai-sdk.ts

aiSdkExample

Copy-paste tool() calls using jsonSchema. Free.

POST /mcp

mcp

Tools: convert and units are free. parse, batch, receipt, compare-prices, scale-recipe, size, date, and estimate are paid with the same x402 prices as the HTTP routes. Schemas match /tools/openai.json and /tools/anthropic.json. A paid tool call without payment returns payment_required and does not include the result.