81 endpoints v1.5.0 OpenAPI

A bucket of small, fast JSON APIs.

Time and timezones, encoding and hashing, colour maths, statistics, checksum validation, QR codes, placeholder data and reference datasets — all as plain JSON over HTTPS. No signup, no API key, no client library. Every endpoint on this page is live: press Run and you are calling the real thing.

Quick start
curl -s https://jsonbucket.com/v1/time?tz=Europe/Stockholm

Time

GET /v1/time

Current time in any timezone

Returns the current instant rendered in the requested IANA timezone, with the calendar fields most callers end up computing themselves.

ParameterTypeDescriptionValue
tz string IANA timezone name default UTC
Example response
{
  "abbreviation": "CEST",
  "date": "2026-08-07",
  "datetime": "2026-08-07T22:00:00+02:00",
  "day_of_year": 219,
  "is_dst": true,
  "iso_week": 32,
  "iso_year": 2026,
  "leap_year": false,
  "offset_seconds": 7200,
  "time": "22:00:00",
  "timezone": "Europe/Stockholm",
  "unix": 1786312800,
  "unix_ms": 1786312800000,
  "utc_datetime": "2026-08-07T20:00:00Z",
  "utc_offset": "+02:00",
  "weekday": "Friday"
}
GET /v1/time/zones

List IANA timezone names

ParameterTypeDescriptionValue
q string Case-insensitive substring filter
Example response
{
  "count": 2,
  "total": 517,
  "zones": [
    "Europe/Stockholm",
    "Arctic/Longyearbyen"
  ]
}
GET /v1/time/convert

Convert an instant between timezones

ParameterTypeDescriptionValue
time string Instant to convert; defaults to now. RFC 3339, a unix timestamp, or a common date format
from string Timezone the input is in, when it carries no offset default UTC
to required string Target IANA timezone
Example response
{
  "day_changed": true,
  "from": {
    "abbreviation": "UTC",
    "datetime": "2026-08-07T20:00:00Z",
    "timezone": "UTC",
    "utc_offset": "+00:00"
  },
  "input": "2026-08-07T20:00:00Z",
  "to": {
    "abbreviation": "JST",
    "datetime": "2026-08-08T05:00:00+09:00",
    "timezone": "Asia/Tokyo",
    "utc_offset": "+09:00"
  },
  "unix": 1786233600
}
GET /v1/time/diff

Duration between two instants

ParameterTypeDescriptionValue
from required string Start instant
to string End instant; defaults to now
Example response
{
  "calendar": {
    "days": 6,
    "hours": 20,
    "minutes": 0,
    "months": 7,
    "years": 0
  },
  "days": 221.75,
  "from": "2026-01-01T00:00:00Z",
  "hours": 5322,
  "humanized": "7 months, 6 days",
  "is_negative": false,
  "minutes": 319320,
  "seconds": 19159200,
  "to": "2026-08-07T20:00:00Z"
}
GET /v1/time/parse

Parse a messy date string

Tries a long list of common layouts, including unix timestamps in seconds and milliseconds, and reports which one matched.

ParameterTypeDescriptionValue
time required string Date or timestamp to parse
tz string Timezone to assume when the input carries no offset default UTC
Example response
{
  "input": "7 Aug 2026 22:00",
  "matched_layout": "2 Jan 2006 15:04",
  "rfc3339": "2026-08-07T22:00:00+02:00",
  "timezone": "Europe/Stockholm",
  "unix": 1786136400,
  "weekday": "Friday"
}
GET /v1/time/zone

Details for one timezone, including its clock changes

Current offset and abbreviation, plus every transition in the year — the dates the clocks move and what they move to. Zones without daylight saving report none, which is an answer rather than an omission. /v1/time/zones lists the names.

ParameterTypeDescriptionValue
name required string IANA timezone name
year integer Year to list transitions for; defaults to the current one
Example response
{
  "name": "Europe/Stockholm",
  "now": {
    "abbreviation": "CEST",
    "datetime": "2026-08-11T14:00:00+02:00",
    "is_dst": true,
    "offset_seconds": 7200,
    "utc_offset": "+02:00"
  },
  "observes_dst": true,
  "transitions": [
    {
      "at": "2026-03-29T03:00:00+02:00",
      "from_abbreviation": "CET",
      "from_offset": "+01:00",
      "kind": "start",
      "shift_seconds": 3600,
      "to_abbreviation": "CEST",
      "to_offset": "+02:00"
    },
    {
      "at": "2026-10-25T02:00:00+01:00",
      "from_abbreviation": "CEST",
      "from_offset": "+02:00",
      "kind": "end",
      "shift_seconds": -3600,
      "to_abbreviation": "CET",
      "to_offset": "+01:00"
    }
  ],
  "year": 2026
}
GET /v1/time/sun

Sunrise, sunset and twilight for a place and date

Times are computed from the date and coordinate, so this works anywhere without a lookup. Above the polar circles the sun may not rise or set at all; those days report what actually happens rather than inventing a time.

ParameterTypeDescriptionValue
lat required number Latitude, -90 to 90
lon required number Longitude, -180 to 180
date string Date to compute, YYYY-MM-DD; defaults to today
tz string Render the times in this IANA timezone default UTC
Example response
{
  "civil_twilight": {
    "dawn": "2026-06-21T01:59:00+02:00",
    "dusk": "2026-06-21T23:40:00+02:00"
  },
  "date": "2026-06-21",
  "day_length": "18h37m0s",
  "day_length_seconds": 67020,
  "golden_hour": {
    "evening_starts": "2026-06-21T20:52:00+02:00",
    "morning_ends": "2026-06-21T04:47:00+02:00"
  },
  "latitude": 59.33,
  "longitude": 18.06,
  "nautical_twilight": {
    "dawn": null,
    "dusk": null
  },
  "polar_day": false,
  "polar_night": false,
  "solar_noon": "2026-06-21T12:49:00+02:00",
  "sunrise": "2026-06-21T03:31:00+02:00",
  "sunset": "2026-06-21T22:08:00+02:00",
  "timezone": "Europe/Stockholm"
}
GET /v1/time/cron

Explain a cron expression and list its next runs

Parses a standard five-field cron expression, or a @macro such as @daily, and returns a plain-English description plus the next fire times in the timezone you choose.

ParameterTypeDescriptionValue
expr required string Cron expression, five fields or an @macro
tz string IANA timezone the schedule runs in default UTC
count integer How many upcoming runs to return, 1-50 default 5
Example response
{
  "description": "Every 15 minutes, between 09:00 and 17:59, Monday through Friday",
  "expression": "*/15 9-17 * * 1-5",
  "fields": {
    "day_of_month": "*",
    "day_of_week": "1-5",
    "hour": "9-17",
    "minute": "*/15",
    "month": "*"
  },
  "next_runs": [
    "2026-08-10T09:00:00+02:00",
    "2026-08-10T09:15:00+02:00"
  ],
  "timezone": "Europe/Stockholm"
}

Network

GET /v1/ip

Your public IP address, and where it looks up to

Returns the address the request arrived from, enriched with the country the edge resolved it to. Behind a reverse proxy this reads X-Forwarded-For, but only when the immediate peer is a configured trusted proxy — otherwise a caller could name its own address and country.

ParameterTypeDescriptionValue
format string json or text; text returns the bare address for shell use default json
Example response
{
  "country": {
    "code": "SE",
    "flag": "🇸🇪",
    "name": "Sweden",
    "region": "Europe",
    "subregion": "Northern Europe"
  },
  "ip": "203.0.113.42",
  "is_loopback": false,
  "is_private": false,
  "version": 4
}
GET /v1/cidr

Subnet calculator

Expands a CIDR block into its network address, broadcast address, usable host range and host count. Works for IPv4 and IPv6.

ParameterTypeDescriptionValue
cidr required string Network in CIDR notation
Example response
{
  "broadcast_address": "192.168.1.255",
  "cidr": "192.168.1.0/24",
  "first_usable": "192.168.1.1",
  "is_private": true,
  "last_usable": "192.168.1.254",
  "netmask": "255.255.255.0",
  "network_address": "192.168.1.0",
  "prefix_length": 24,
  "total_addresses": "256",
  "usable_hosts": "254",
  "version": 4,
  "wildcard_mask": "0.0.0.255"
}
GET /v1/ip/validate

Classify an IP address

Reports the version and every special-use range the address falls in — private, loopback, link-local, multicast, CGNAT or documentation.

ParameterTypeDescriptionValue
ip required string IPv4 or IPv6 address
Example response
{
  "ip": "100.64.0.1",
  "is_cgnat": true,
  "is_documentation": false,
  "is_global_unicast": true,
  "is_link_local": false,
  "is_loopback": false,
  "is_multicast": false,
  "is_private": false,
  "scope": "carrier-grade NAT (RFC 6598)",
  "valid": true,
  "version": 4
}

Inspect

GETPOST /v1/echo

Echo the request back to you

Returns the method, path, query, headers and body exactly as this server received them. The quickest way to find out what your client, proxy or CDN is actually sending.

Example response
{
  "body": "",
  "body_length": 0,
  "client_ip": "203.0.113.42",
  "headers": {
    "Accept": "application/json",
    "User-Agent": "curl/8.5.0"
  },
  "host": "api.example.com",
  "method": "GET",
  "path": "/v1/echo",
  "protocol": "HTTP/1.1",
  "query": {
    "hello": "world"
  }
}
GET /v1/headers

The request headers you sent

Example response
{
  "count": 3,
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip",
    "User-Agent": "curl/8.5.0"
  }
}
GET /v1/status/{code}

Respond with the status code you ask for

Useful for exercising a client's error and retry paths. Any code between 200 and 599 is returned verbatim.

ParameterTypeDescriptionValue
code path required integer HTTP status code to return, 200-599
Example response
{
  "description": "The server is temporarily overloaded or down for maintenance. Send Retry-After.",
  "name": "Service Unavailable",
  "status": 503
}
GET /v1/delay/{seconds}

Respond after a delay

Holds the response open for the requested number of seconds, for testing client timeouts. Capped at 10 seconds.

ParameterTypeDescriptionValue
seconds path required number Seconds to wait, 0-10 (fractions allowed)
Example response
{
  "delay_seconds": 2,
  "slept_ms": 2001
}
GET /v1/user-agent

Parse a User-Agent string

Parses the caller's User-Agent, or one supplied via ?ua=. Detection is heuristic — user agent strings are a pile of historical fiction — so treat the result as a strong hint rather than fact.

ParameterTypeDescriptionValue
ua string User-Agent to parse instead of your own
Example response
{
  "browser": "Chrome",
  "browser_version": "120.0",
  "device": "desktop",
  "is_bot": false,
  "os": "macOS",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
}

Random

GET /v1/uuid

Generate UUIDs

Version 4 UUIDs are fully random. Version 7 embeds a millisecond timestamp in the high bits, so they sort chronologically — useful as database keys.

ParameterTypeDescriptionValue
v string UUID version: 4 or 7 default 4
count integer How many to generate, 1-1000 default 1
Example response
{
  "count": 2,
  "uuids": [
    "1f7c4b2e-8a3d-4f19-9c07-2b5e6d81a4f3",
    "9e2a10c5-7b64-4d8e-a3f1-05c9b7e24d68"
  ],
  "version": 4
}
GET /v1/random/number

Random integers in a range

ParameterTypeDescriptionValue
min integer Lower bound, inclusive default 1
max integer Upper bound, inclusive default 100
count integer How many to draw, 1-1000 default 1
Example response
{
  "count": 5,
  "max": 6,
  "min": 1,
  "numbers": [
    4,
    1,
    6,
    3,
    3
  ]
}
GET /v1/random/string

Random string from a charset

ParameterTypeDescriptionValue
length integer Characters to generate, 1-4096 default 16
charset string alnum, alpha, numeric or hex default alnum
count integer How many strings, 1-1000 default 1
Example response
{
  "charset": "alnum",
  "count": 2,
  "length": 16,
  "strings": [
    "k2Rm9xQpZ4vLd7Ta",
    "B8nWc3yHs6Ug1Jre"
  ]
}
GET /v1/password

Generate passwords with an honest entropy estimate

Memorable passwords are drawn from the EFF short diceware list; random ones from a character set. Entropy is computed from the actual pool size, so you can see what you are really getting.

ParameterTypeDescriptionValue
type string memorable or random default memorable
words integer Words per memorable password, 3-12 default 4
length integer Characters per random password, 8-128 default 16
separator string Separator between words default -
count integer How many to generate, 1-50 default 1
Example response
{
  "count": 2,
  "entropy_bits": 41.4,
  "passwords": [
    "acorn-drift-mocha-vivid",
    "pouch-ridge-vocal-swan"
  ],
  "pool_size": 1295,
  "strength": "strong",
  "type": "memorable"
}

Generate

GETPOST /v1/qr

Generate a QR code

Returns SVG by default, which stays sharp at any size and embeds straight into HTML. Ask for png to get a raster image, or json to get the raw module grid and draw it yourself.

ParameterTypeDescriptionValue
text required string Content to encode. Alternatively POST it as the body.
format string svg, png or json default svg
size integer Image size in pixels, 64-1024 default 256
level string Error correction: low, medium, high or highest default medium
dark string Module colour default #000000
light string Background colour; use none for transparent default #ffffff
Example response
{
  "format": "json",
  "level": "medium",
  "modules": 25,
  "note": "Set format=svg or format=png to receive the image itself.",
  "text": "https://example.com"
}
GET /v1/badge

Generate an SVG status badge

The familiar two-tone README badge, rendered server-side as SVG. Drop the URL straight into a Markdown image tag.

ParameterTypeDescriptionValue
label string Left-hand text default build
value required string Right-hand text
color string Right-hand colour: a name like brightgreen, or any hex value default brightgreen
label_color string Left-hand colour default #555
style string flat or square default flat
Example response
{
  "note": "Returns image/svg+xml. Embed with ![build](/v1/badge?label=build\u0026value=passing)"
}
GET /v1/placeholder/{size}

Placeholder image as SVG

A sized, labelled rectangle for mocking up a layout. SVG rather than a bitmap, so it stays sharp at any scale and costs a few hundred bytes. The label defaults to the dimensions, and the text scales with the box so it fits a favicon and a banner alike.

ParameterTypeDescriptionValue
size path required string WIDTHxHEIGHT, or one number for a square. 1 to 4000.
text string Label to draw; defaults to the dimensions
bg string Background colour, named or hex default #e2e8f0
fg string Text colour, named or hex default #64748b
format string svg for the image, or json to get the URL and metadata back default svg
Example response
{
  "note": "Returns image/svg+xml. Embed with \u003cimg src=\"/v1/placeholder/640x360\"\u003e. Add format=json for the dimensions as data."
}

Fake

GET /v1/fake/person

Generate fake people

Placeholder records for seeding a database or filling a demo. Every value is invented: email domains are RFC 2606 reserved names, so nothing here can reach a real person.

ParameterTypeDescriptionValue
locale string en or sv default en
count integer How many records to generate, 1-100 default 1
sex string Restrict to female or male given names
Example response
{
  "count": 1,
  "locale": "sv",
  "people": [
    {
      "address": {
        "city": "Uppsala",
        "country": "Sweden",
        "country_code": "SE",
        "postcode": "752 37",
        "region": "Uppsala",
        "street": "Björkgatan 14"
      },
      "email": "[email protected]",
      "first_name": "Astrid",
      "full_name": "Astrid Lindqvist",
      "job_title": "Data Engineer",
      "last_name": "Lindqvist",
      "phone": "+46 70 123 45 67",
      "sex": "female",
      "username": "astrid.lindqvist"
    }
  ]
}
GET /v1/fake/address

Generate fake addresses

ParameterTypeDescriptionValue
locale string en or sv default en
count integer How many records to generate, 1-100 default 1
Example response
{
  "addresses": [
    {
      "city": "Fairview",
      "country": "United States",
      "country_code": "US",
      "postcode": "97204",
      "region": "Oregon",
      "street": "42 Maple Avenue"
    }
  ],
  "count": 1,
  "locale": "en"
}
GET /v1/fake/company

Generate fake companies

ParameterTypeDescriptionValue
locale string en or sv default en
count integer How many records to generate, 1-100 default 1
Example response
{
  "companies": [
    {
      "domain": "cobaltfoundry.example.com",
      "email": "[email protected]",
      "employees": 240,
      "founded": 2011,
      "industry": "Manufacturing",
      "name": "Cobalt Foundry Ltd."
    }
  ],
  "count": 1,
  "locale": "en"
}

JSON

GETPOST /v1/json/format

Pretty-print or minify JSON

POST the document, or pass it as ?json= for something short. Sorting keys makes two documents comparable when only their key order differs.

ParameterTypeDescriptionValue
json string The document. Prefer the request body for anything sizeable.
mode string pretty or minify default pretty
indent integer Spaces per level when pretty-printing, 0-8 default 2
sort_keys boolean Order object keys alphabetically default false
Example response
{
  "bytes_in": 13,
  "bytes_out": 24,
  "formatted": "{\n  \"a\": 1,\n  \"b\": 2\n}",
  "mode": "pretty",
  "size_change": "+84.6%"
}
GETPOST /v1/json/validate

Check JSON, and say where it breaks

Reports the line and column of the first problem, with the offending text quoted. Being told only "unexpected token" leaves you counting characters by hand. Invalid input is a successful answer to the question, so this returns 200 either way.

ParameterTypeDescriptionValue
json string The document. Prefer the request body.
Example response
{
  "column": 9,
  "error": "invalid character '}' looking for beginning of object key string",
  "excerpt": "{\"a\": 1,}",
  "line": 1,
  "offset": 8,
  "pointer": "        ^",
  "valid": false
}
GETPOST /v1/json/schema

Infer a JSON Schema from a document

Produces a draft 2020-12 schema describing the sample. Strings are checked for recognisable formats, and array items are merged so a list of similar objects yields one description rather than many.

ParameterTypeDescriptionValue
json string The sample document. Prefer the request body.
required boolean Mark every observed key as required default true
Example response
{
  "schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "properties": {
      "email": {
        "format": "email",
        "type": "string"
      },
      "id": {
        "type": "integer"
      }
    },
    "required": [
      "email",
      "id"
    ],
    "type": "object"
  }
}
POST /v1/json/diff

Structural diff between two documents

Reports which values changed rather than which lines, so reordered keys and reformatting are not differences. Paths are JSON Pointers (RFC 6901). POST {"from": ..., "to": ...}.

Example response
{
  "changes": 3,
  "diff": [
    {
      "from": "old",
      "op": "replace",
      "path": "/name",
      "to": "new"
    },
    {
      "op": "add",
      "path": "/tags/2",
      "to": "z"
    },
    {
      "from": true,
      "op": "remove",
      "path": "/legacy"
    }
  ],
  "equal": false
}
GETPOST /v1/json/query

Pull values out of a document by path

Supports $ for the root, .key and ["key"] for members, [0] and [-1] for elements counting from either end, [*] for every element or value, and .. for a recursive search. Filter expressions are not supported — this is a path lookup, not a query language.

ParameterTypeDescriptionValue
path required string The path to extract
json string The document. Prefer the request body.
Example response
{
  "count": 2,
  "matches": [
    "a",
    "b"
  ],
  "path": "$.items[*].name",
  "paths": [
    "/items/0/name",
    "/items/1/name"
  ]
}
GETPOST /v1/csv/to-json

Convert CSV to JSON

The first row names the columns unless header=false, in which case rows come back as arrays. Values that look like numbers or booleans are converted, except where that would lose information — a leading zero is kept as text, because 00123 is an identifier and 123 is not.

ParameterTypeDescriptionValue
csv string The CSV. Prefer the request body.
delimiter string Field separator. Use "tab" for tab-separated. default ,
header boolean Treat the first row as column names default true
types boolean Convert numbers and booleans instead of leaving every value a string default true
Example response
{
  "columns": [
    "name",
    "age",
    "member"
  ],
  "data": [
    {
      "age": 36,
      "member": true,
      "name": "Ada"
    },
    {
      "age": 27,
      "member": false,
      "name": "Linus"
    }
  ],
  "rows": 2
}
POST /v1/json/to-csv

Convert JSON to CSV

Takes an array of objects. Columns are every key seen across every object, in alphabetical order — a JSON object has no inherent key order to preserve, so sorting is the only stable choice. Objects missing a column get an empty field, and nested values are written as JSON, since CSV cannot hold them.

ParameterTypeDescriptionValue
delimiter string Field separator. Use "tab" for tab-separated. default ,
header boolean Write the column names as the first row default true
Example response
{
  "columns": [
    "name",
    "age"
  ],
  "csv": "name,age\nAda,36\nLinus,27\n",
  "rows": 2
}
POST /v1/yaml/to-json

Convert YAML to JSON

Returns the document as JSON. A file holding several documents separated by --- comes back as an array, so a Kubernetes manifest survives intact rather than losing everything after the first. YAML permits keys JSON cannot hold — numbers, booleans, whole structures — so those become their text form and are listed in converted_keys rather than changing silently.

Example response
{
  "converted_keys": [],
  "documents": 1,
  "formatted": "{\n  \"name\": \"ada\",\n  \"tags\": [\n    \"x\",\n    \"y\"\n  ]\n}",
  "json": {
    "name": "ada",
    "tags": [
      "x",
      "y"
    ]
  }
}
GETPOST /v1/json/to-yaml

Convert JSON to YAML

ParameterTypeDescriptionValue
json string The document. Prefer the request body.
indent integer Spaces per level, 1-8 default 2
Example response
{
  "bytes": 30,
  "yaml": "name: ada\ntags:\n    - x\n    - y\n"
}
GETPOST /v1/json/flatten

Flatten nested JSON to one level, or expand it back

Flattening turns {"a":{"b":1}} into {"a.b":1}, with array elements addressed by index. Expanding reverses it. Useful for environment variables, spreadsheets and anything that cannot nest.

ParameterTypeDescriptionValue
json string The document. Prefer the request body.
mode string flatten or expand default flatten
separator string Joins the key segments default .
Example response
{
  "keys": 3,
  "mode": "flatten",
  "result": {
    "a.b": 1,
    "c.0": 10,
    "c.1": 20
  }
}

Text

GETPOST /v1/slugify

Turn text into a URL slug

Unicode-aware: accented letters are folded to their ASCII base (Skära becomes skara), and anything left over is dropped.

ParameterTypeDescriptionValue
text string Text to slugify. Alternatively POST it as the body.
separator string Character to join words with default -
Example response
{
  "input": "Räksmörgås på Skärgården!",
  "slug": "raksmorgas-pa-skargarden"
}
GETPOST /v1/case

Convert between naming conventions

Returns every common case style at once, so you can pick the one you needed without asking twice.

ParameterTypeDescriptionValue
text string Text to convert. Alternatively POST it as the body.
Example response
{
  "camel": "helloWorldExample",
  "constant": "HELLO_WORLD_EXAMPLE",
  "dot": "hello.world.example",
  "input": "hello world example",
  "kebab": "hello-world-example",
  "lower": "hello world example",
  "pascal": "HelloWorldExample",
  "sentence": "Hello world example",
  "snake": "hello_world_example",
  "title": "Hello World Example",
  "upper": "HELLO WORLD EXAMPLE"
}
GET /v1/lorem

Generate placeholder text

ParameterTypeDescriptionValue
paragraphs integer Number of paragraphs, 1-50 default 3
sentences integer Sentences per paragraph, 1-30 default 5
words integer If set, return exactly this many words instead, 1-2000 default 0
Example response
{
  "paragraphs": [
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
  ],
  "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
  "word_count": 8
}
GETPOST /v1/url/parse

Split a URL into its parts

Breaks a URL into scheme, host, port, path, query and fragment, with the query decoded into an object. The URL is only parsed — nothing is fetched. Repeated query keys are kept as a list rather than collapsed to the last value, and anything worth a second look is called out: credentials in the URL, a non-ASCII or punycode host, a port that is already the default.

ParameterTypeDescriptionValue
value required string The URL to parse. Percent-encode it, or send it as a POST body.
Example response
{
  "absolute": true,
  "default_port": 443,
  "fragment": "top",
  "has_password": false,
  "host": "example.com",
  "host_type": "domain",
  "input": "https://[email protected]:8443/a/b?q=go\u0026tag=x\u0026tag=y#top",
  "notes": [
    "credentials are embedded in the URL"
  ],
  "path": "/a/b",
  "path_segments": [
    "a",
    "b"
  ],
  "port": 8443,
  "port_is_default": false,
  "query": {
    "q": "go",
    "tag": [
      "x",
      "y"
    ]
  },
  "query_string": "q=go\u0026tag=x\u0026tag=y",
  "scheme": "https",
  "tld": "com",
  "username": "user",
  "valid": true
}

Encoding

GETPOST /v1/hash

Hash text with several algorithms at once

Returns every digest in one response, so you do not have to guess which one you needed. POST the raw body to hash bytes that do not survive a query string.

ParameterTypeDescriptionValue
text string Input text. Alternatively POST the raw bytes as the request body.
Example response
{
  "crc32": "0d4a1185",
  "input_length": 11,
  "md5": "5eb63bbbe01eeed093cb22bb8f5acdc3",
  "sha1": "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
  "sha256": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
}
GETPOST /v1/encode/{scheme}

Encode text

ParameterTypeDescriptionValue
scheme path required string base64, base64url, hex, url or html
text string Input text. Alternatively POST the raw bytes as the request body.
Example response
{
  "input": "hello world",
  "output": "aGVsbG8gd29ybGQ=",
  "scheme": "base64"
}
GETPOST /v1/decode/{scheme}

Decode text

ParameterTypeDescriptionValue
scheme path required string base64, base64url, hex, url or html
text string Encoded input. Alternatively POST it as the request body.
Example response
{
  "input": "aGVsbG8gd29ybGQ=",
  "output": "hello world",
  "scheme": "base64"
}
POST /v1/jwt/decode

Decode a JWT without verifying it

Splits the token and decodes its header and payload, expanding the standard time claims. The signature is NOT verified — this tells you what a token says, never whether to trust it. POST only, so tokens stay out of browser history and proxy logs.

Example response
{
  "claims": {
    "expires_at": "2018-01-18T02:30:22Z",
    "is_expired": true,
    "issued_at": "2018-01-18T01:30:22Z"
  },
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "iat": 1516239022,
    "name": "Ada Lovelace",
    "sub": "1234567890"
  },
  "signature": "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "signature_verified": false
}

Color

GET /v1/color/convert

Convert a colour between every common notation

ParameterTypeDescriptionValue
color required string Hex (#2f6feb), rgb(47,111,235), hsl(220,83%,55%) or a CSS colour name
Example response
{
  "cmyk": {
    "c": 80,
    "k": 7.8,
    "m": 52.8,
    "y": 0
  },
  "css": {
    "hex": "#2f6feb",
    "hsl": "hsl(219.6, 82.5%, 55.3%)",
    "oklch": "oklch(0.573 0.199 261.8)",
    "rgb": "rgb(47, 111, 235)"
  },
  "hex": "#2f6feb",
  "hsl": {
    "h": 219.6,
    "l": 55.3,
    "s": 82.5
  },
  "hsv": {
    "h": 219.6,
    "s": 80,
    "v": 92.2
  },
  "input": "#2f6feb",
  "is_dark": false,
  "luminance": 0.1797,
  "nearest_css_name": "royalblue",
  "oklch": {
    "c": 0.199,
    "h": 261.8,
    "l": 0.573
  },
  "rgb": {
    "b": 235,
    "g": 111,
    "r": 47
  }
}
GET /v1/color/contrast

WCAG contrast ratio between two colours

Returns the ratio and whether it clears WCAG 2.1 AA and AAA for normal and large text. This is the calculation people most often get wrong by hand — the channel values need linearising first.

ParameterTypeDescriptionValue
foreground required string Text colour
background required string Background colour
Example response
{
  "background": "#ffffff",
  "foreground": "#767676",
  "passes": {
    "aa_large": true,
    "aa_normal": true,
    "aaa_large": true,
    "aaa_normal": false
  },
  "ratio": 4.54,
  "ratio_label": "4.54:1",
  "verdict": "Passes AA for all text, AAA for large text only."
}
GET /v1/color/palette

Generate a harmonious palette from one colour

ParameterTypeDescriptionValue
color required string Hex (#2f6feb), rgb(47,111,235), hsl(220,83%,55%) or a CSS colour name
scheme string complementary, analogous, triadic, tetradic, monochromatic or shades default analogous
count integer Colours to return for monochromatic and shades, 2-12 default 5
Example response
{
  "colors": [
    {
      "hex": "#2f6feb",
      "rgb": "rgb(47, 111, 235)",
      "role": "base"
    },
    {
      "hex": "#eb2f6f",
      "rgb": "rgb(235, 47, 111)",
      "role": "triadic 1"
    },
    {
      "hex": "#6feb2f",
      "rgb": "rgb(111, 235, 47)",
      "role": "triadic 2"
    }
  ],
  "input": "#2f6feb",
  "scheme": "triadic"
}

Statistics

GETPOST /v1/statistics/summary

Descriptive statistics for a set of numbers

Pass values in the query string for quick use, or POST a JSON body of the form {"values": [1, 2, 3]} for larger sets (up to 100,000 numbers).

ParameterTypeDescriptionValue
values string Comma-separated numbers. For large sets, POST {"values": [...]} instead.
Example response
{
  "count": 5,
  "iqr": 2,
  "max": 100,
  "mean": 22,
  "median": 3,
  "min": 1,
  "population_stddev": 38.642,
  "population_variance": 1493.2,
  "q1": 2,
  "q3": 4,
  "range": 99,
  "stddev": 43.203,
  "sum": 110,
  "variance": 1866.5
}
GETPOST /v1/statistics/percentile

Percentiles of a set of numbers

Uses linear interpolation between the closest ranks, matching the default behaviour of NumPy and most statistics packages.

ParameterTypeDescriptionValue
values string Comma-separated numbers. For large sets, POST {"values": [...]} instead.
p string One or more percentiles in 0-100, comma-separated default 50
Example response
{
  "count": 5,
  "percentiles": {
    "50": 3,
    "90": 61.6,
    "99": 96.16
  }
}
GETPOST /v1/statistics/histogram

Bucket values into a histogram

Returns bucket boundaries and counts, plus an ASCII bar for each bucket so the shape is readable straight from a terminal.

ParameterTypeDescriptionValue
values string Comma-separated numbers, or POST {"values": [...]}
bins integer Number of buckets, 1-100 default 10
Example response
{
  "bin_width": 0.8,
  "bins": 5,
  "buckets": [
    {
      "bar": "██",
      "count": 1,
      "lower": 1,
      "upper": 1.8
    },
    {
      "bar": "████",
      "count": 2,
      "lower": 1.8,
      "upper": 2.6
    }
  ],
  "count": 9,
  "max": 5,
  "min": 1
}
GETPOST /v1/statistics/regression

Least-squares linear regression

ParameterTypeDescriptionValue
x required string Comma-separated independent values
y required string Comma-separated dependent values, same length as x
Example response
{
  "count": 5,
  "equation": "y = 0.6x + 2.2",
  "intercept": 2.2,
  "r": 0.7746,
  "r_squared": 0.6,
  "slope": 0.6,
  "standard_error": 0.4216
}
GETPOST /v1/statistics/correlation

Pearson and Spearman correlation

ParameterTypeDescriptionValue
x required string Comma-separated independent values
y required string Comma-separated dependent values, same length as x
Example response
{
  "count": 5,
  "interpretation": "strong positive",
  "pearson": 0.7746,
  "spearman": 0.8208
}
GETPOST /v1/statistics/outliers

Find outliers by IQR or z-score

ParameterTypeDescriptionValue
values string Comma-separated numbers, or POST {"values": [...]}
method string iqr or zscore default iqr
threshold number IQR multiplier, or z-score cutoff when method=zscore default 1.5
Example response
{
  "clean": [
    1,
    2,
    3,
    4,
    5
  ],
  "count": 6,
  "lower_bound": -2.375,
  "method": "iqr",
  "outlier_count": 1,
  "outliers": [
    100
  ],
  "threshold": 1.5,
  "upper_bound": 9.625
}

Math

GET /v1/math/convert

Convert between units of measurement

Covers length, mass, temperature, data, time, speed, area, volume, pressure and energy. Binary and decimal data prefixes are distinct: a MB is 1,000,000 bytes and a MiB is 1,048,576.

ParameterTypeDescriptionValue
value required number Quantity to convert
from required string Source unit
to required string Target unit, in the same category
Example response
{
  "category": "length",
  "formatted": "62.137119 mi",
  "from": "km",
  "from_name": "kilometre",
  "result": 62.137119,
  "to": "mi",
  "to_name": "mile",
  "value": 100
}
GET /v1/math/units

List the units available for conversion

ParameterTypeDescriptionValue
category string Restrict to one category
Example response
{
  "categories": [
    "area",
    "data",
    "energy",
    "length"
  ],
  "units": {
    "length": [
      {
        "name": "kilometre",
        "symbol": "km"
      }
    ]
  }
}
GET /v1/math/base

Convert a number between bases

Converts between any two bases from 2 to 36, and always shows binary, octal, decimal and hexadecimal alongside. Arbitrary precision, so a value far past 64 bits converts exactly rather than wrapping. Underscores and spaces in the input are ignored, so a grouped literal like 1010_1010 works.

ParameterTypeDescriptionValue
value required string The number to convert
from integer Base of the input, 2 to 36. Prefixes 0x, 0b and 0o set it too. default 10
to integer Base to convert to, 2 to 36. Omit for the common four only.
Example response
{
  "bases": {
    "binary": "11111111",
    "decimal": "255",
    "hexadecimal": "ff",
    "octal": "377"
  },
  "bits": 8,
  "decimal": "255",
  "from": 10,
  "input": "255",
  "negative": false,
  "result": "ff",
  "to": 16
}

Validate

GETPOST /v1/validate/iban

Validate an IBAN

Checks the country-specific length and the ISO 7064 mod-97 checksum, then splits out the bank and account parts.

ParameterTypeDescriptionValue
value required string IBAN, with or without spaces
Example response
{
  "bban": "WEST12345698765432",
  "check_digits": "82",
  "country": "GB",
  "country_name": "United Kingdom",
  "formatted": "GB82 WEST 1234 5698 7654 32",
  "iban": "GB82WEST12345698765432",
  "input": "GB82 WEST 1234 5698 7654 32",
  "length": 22,
  "valid": true
}
GETPOST /v1/validate/isbn

Validate an ISBN and convert between ISBN-10 and ISBN-13

ParameterTypeDescriptionValue
value required string ISBN-10 or ISBN-13, hyphens allowed
Example response
{
  "input": "978-0-13-235088-4",
  "isbn10": "0132350882",
  "isbn13": "9780132350884",
  "prefix": "978",
  "type": "ISBN-13",
  "valid": true
}
GETPOST /v1/validate/luhn

Luhn checksum and card scheme detection

Verifies the Luhn check digit and names the issuing scheme from the leading digits. Nothing is stored or logged — the query string never reaches the access log — but send test numbers, not live ones.

ParameterTypeDescriptionValue
value required string Digit string, spaces and hyphens allowed
Example response
{
  "check_digit": "1",
  "digits": 16,
  "input": "4111 1111 1111 1111",
  "scheme": "Visa",
  "valid": true
}
GETPOST /v1/validate/personnummer

Validate a Swedish personnummer or organisationsnummer

Handles personnummer, samordningsnummer (coordination numbers, where the day has 60 added) and organisationsnummer, all of which carry a Luhn check digit. Send test numbers only.

ParameterTypeDescriptionValue
value required string 10 or 12 digits, hyphen or plus optional
Example response
{
  "age": 44,
  "birth_date": "1981-12-18",
  "century_assumed": true,
  "formatted": "811218-9876",
  "input": "811218-9876",
  "normalized": "198112189876",
  "type": "personnummer",
  "valid": true
}
GETPOST /v1/validate/uuid

Inspect a UUID: version, variant and embedded time

Reads back what /v1/uuid writes. Versions 1, 6 and 7 carry a timestamp, which is returned — worth knowing, because a v1 or v7 identifier leaks when it was created and is a poor choice for anything meant to be unguessable.

ParameterTypeDescriptionValue
value required string UUID, with or without hyphens or surrounding braces
Example response
{
  "hex": "018f6c7a1b2c7d3e8f401a2b3c4d5e6f",
  "input": "018f6c7a-1b2c-7d3e-8f40-1a2b3c4d5e6f",
  "is_max": false,
  "is_nil": false,
  "random_source": true,
  "time_ordered": true,
  "timestamp": "2024-05-12T11:04:52.780Z",
  "timestamp_ms": 1715511892780,
  "urn": "urn:uuid:018f6c7a-1b2c-7d3e-8f40-1a2b3c4d5e6f",
  "uuid": "018f6c7a-1b2c-7d3e-8f40-1a2b3c4d5e6f",
  "valid": true,
  "variant": "RFC 4122",
  "version": 7,
  "version_description": "time-ordered, random (v7)"
}
GET /v1/semver

Parse a semantic version, compare two, or test a range

Splits a version into its parts, and with a second version compares them by the semver.org rules. Prerelease ordering is the part that catches people: 1.0.0-alpha.10 is newer than 1.0.0-alpha.2 because numeric identifiers compare as numbers, and any prerelease is older than the release it precedes. Build metadata is parsed and then ignored in comparisons, as the spec requires.

ParameterTypeDescriptionValue
version required string Version to parse
compare string Second version to compare the first against
range string Range to test the version against: ^1.2.0, ~1.2, >=1.2 <2.0.0
Example response
{
  "build": "build.7",
  "input": "1.4.2-rc.1+build.7",
  "is_prerelease": true,
  "is_stable": false,
  "major": 1,
  "minor": 4,
  "next": {
    "major": "2.0.0",
    "minor": "1.5.0",
    "patch": "1.4.3"
  },
  "patch": 2,
  "prerelease": "rc.1",
  "prerelease_ids": [
    "rc",
    1
  ],
  "release": "1.4.2",
  "valid": true,
  "version": "1.4.2-rc.1+build.7"
}

Data

GET /v1/facts/random

Random facts from a curated collection

ParameterTypeDescriptionValue
count integer How many facts to return, 1-50 default 1
category string Restrict to one category
Example response
{
  "categories": [
    "biology",
    "computing",
    "space"
  ],
  "count": 1,
  "facts": [
    {
      "id": 1,
      "category": "space",
      "text": "A day on Venus is longer than its year: it rotates once every 243 Earth days but orbits the Sun every 225."
    }
  ]
}
GET /v1/quotes/random

Random quotations

ParameterTypeDescriptionValue
count integer How many to return, 1-20 default 1
tag string Restrict to one tag
author string Restrict to one author
Example response
{
  "count": 1,
  "quotes": [
    {
      "id": 1,
      "text": "Simplicity is prerequisite for reliability.",
      "author": "Edsger W. Dijkstra",
      "tags": [
        "computing"
      ]
    }
  ],
  "tags": [
    "business",
    "computing",
    "design",
    "life"
  ]
}
GET /v1/jokes/random

Random jokes

ParameterTypeDescriptionValue
count integer How many to return, 1-20 default 1
category string programming, sysadmin or dad
Example response
{
  "categories": [
    "dad",
    "programming",
    "sysadmin"
  ],
  "count": 1,
  "jokes": [
    {
      "id": 1,
      "category": "programming",
      "setup": "Why do programmers prefer dark mode?",
      "punchline": "Because light attracts bugs."
    }
  ]
}
GET /v1/countries

Search the country reference dataset

250 countries and territories with ISO codes, capital, region, population, area, currency, languages and calling code.

ParameterTypeDescriptionValue
q string Case-insensitive match on name, official name or capital
region string Filter by region or subregion
limit integer Maximum results, 1-250 default 50
offset integer Results to skip, for paging default 0
Example response
{
  "count": 1,
  "countries": [
    {
      "name": "Sweden",
      "official_name": "Kingdom of Sweden",
      "iso2": "SE",
      "iso3": "SWE",
      "capital": "Stockholm",
      "region": "Europe",
      "subregion": "Northern Europe",
      "population": 10569709,
      "population_year": 2024,
      "area_km2": 450295,
      "currency_code": "SEK",
      "currency_name": "Swedish krona",
      "currency_symbol": "kr",
      "languages": [
        "Swedish"
      ],
      "calling_code": "+46",
      "tld": ".se",
      "flag": "🇸🇪",
      "latitude": 62,
      "longitude": 15,
      "un_member": true,
      "independent": true
    }
  ],
  "offset": 0,
  "total": 1
}
GET /v1/countries/{code}

Look up one country by ISO code

ParameterTypeDescriptionValue
code path required string ISO 3166-1 alpha-2 or alpha-3 code
Example response
{
  "name": "Sweden",
  "official_name": "Kingdom of Sweden",
  "iso2": "SE",
  "iso3": "SWE",
  "capital": "Stockholm",
  "region": "Europe",
  "subregion": "Northern Europe",
  "population": 10569709,
  "population_year": 2024,
  "area_km2": 450295,
  "currency_code": "SEK",
  "currency_name": "Swedish krona",
  "currency_symbol": "kr",
  "languages": [
    "Swedish"
  ],
  "calling_code": "+46",
  "tld": ".se",
  "flag": "🇸🇪",
  "latitude": 62,
  "longitude": 15,
  "un_member": true,
  "independent": true
}
GET /v1/airports/{code}

Look up an airport by IATA or ICAO code

ParameterTypeDescriptionValue
code path required string Three-letter IATA or four-letter ICAO code
Example response
{
  "iata": "ARN",
  "icao": "ESSA",
  "name": "Stockholm Arlanda Airport",
  "type": "large airport",
  "municipality": "Stockholm",
  "country": "SE",
  "region": "SE-AB",
  "continent": "EU",
  "latitude": 59.651901,
  "longitude": 17.9186,
  "elevation_ft": 137,
  "scheduled_service": true
}
GET /v1/airports

Search airports

Search by name or city, filter by country, or find the airports nearest a coordinate.

ParameterTypeDescriptionValue
q string Match on airport name or municipality
country string ISO 3166-1 alpha-2 country code
near string Latitude,longitude to sort by distance from
scheduled boolean Set to true to return only airports with scheduled service
limit integer Maximum results, 1-200 default 20
Example response
{
  "airports": [
    {
      "iata": "ARN",
      "icao": "ESSA",
      "name": "Stockholm Arlanda Airport",
      "type": "large airport",
      "municipality": "Stockholm",
      "country": "SE",
      "region": "SE-AB",
      "continent": "EU",
      "latitude": 59.651901,
      "longitude": 17.9186,
      "elevation_ft": 137,
      "scheduled_service": true
    }
  ],
  "count": 1,
  "total": 1
}
GET /v1/geo/distance

Distance and bearing between two coordinates

Great-circle distance on a spherical earth, good to a few tenths of a percent. The initial and final bearings differ because a great circle is not a constant heading — follow one east from Stockholm and you end up pointing somewhere else by the time you arrive.

ParameterTypeDescriptionValue
from required string Start point as latitude,longitude
to required string End point as latitude,longitude
Example response
{
  "bearing": {
    "compass": "SW",
    "final": 216.6,
    "initial": 221.3
  },
  "distance": {
    "kilometres": 521.45,
    "metres": 521453.92,
    "miles": 324.02,
    "nautical_miles": 281.56
  },
  "from": {
    "latitude": 59.33,
    "longitude": 18.06
  },
  "to": {
    "latitude": 55.68,
    "longitude": 12.57
  }
}
GET /v1/holidays

Public holidays for a country and year

Computed from calendar rules, including the Gregorian Easter algorithm, so any year works without a lookup table. Statutory holidays and widely observed non-statutory days are distinguished.

ParameterTypeDescriptionValue
country required string ISO 3166-1 alpha-2 code; see the supported list in the response
year integer Year to compute, 1900-2200; defaults to the current year
include_observances boolean Include non-statutory days such as Christmas Eve default true
Example response
{
  "count": 2,
  "country": "SE",
  "holidays": [
    {
      "date": "2026-01-01",
      "local_name": "Nyårsdagen",
      "name": "New Year's Day",
      "observance": false,
      "weekday": "Thursday"
    },
    {
      "date": "2026-04-03",
      "local_name": "Långfredagen",
      "name": "Good Friday",
      "observance": false,
      "weekday": "Friday"
    }
  ],
  "supported_countries": [
    "DE",
    "DK",
    "FI",
    "FR",
    "GB",
    "NL",
    "NO",
    "SE",
    "US"
  ],
  "year": 2026
}

Reference

GET /v1/currencies

ISO 4217 currency reference

Codes, names and minor units — how many decimal places the currency actually has. JPY has none; KWD has three. Getting this wrong is a classic source of rounding bugs. These are static facts, not exchange rates.

ParameterTypeDescriptionValue
q string Match on code or name
Example response
{
  "count": 1,
  "currencies": [
    {
      "code": "SEK",
      "name": "Swedish Krona",
      "numeric": "752",
      "minor_units": 2,
      "symbol": "kr"
    }
  ],
  "total": 178
}
GET /v1/languages

ISO 639 language codes

ParameterTypeDescriptionValue
q string Match on code or name
Example response
{
  "count": 1,
  "languages": [
    {
      "alpha3": "swe",
      "alpha2": "sv",
      "name": "Swedish",
      "french_name": "suédois"
    }
  ],
  "total": 487
}
GET /v1/mime

Look up MIME types by extension or type

ParameterTypeDescriptionValue
extension string File extension, with or without the dot
type string Media type to look up extensions for
Example response
{
  "count": 1,
  "types": [
    {
      "type": "image/webp",
      "extensions": [
        "webp"
      ]
    }
  ]
}
GET /v1/ports

Well-known TCP and UDP ports

ParameterTypeDescriptionValue
port integer Port number to look up
service string Service name to search for
Example response
{
  "count": 2,
  "ports": [
    {
      "port": 443,
      "protocol": "tcp",
      "service": "https",
      "aliases": []
    },
    {
      "port": 443,
      "protocol": "udp",
      "service": "https",
      "aliases": []
    }
  ]
}
GET /v1/http/status

HTTP status code reference

What each code means and which RFC defines it. For a server that actually returns a code, use /v1/status/{code} instead.

ParameterTypeDescriptionValue
code integer Single code to look up
class string Restrict to a class: 1xx, 2xx, 3xx, 4xx or 5xx
Example response
{
  "count": 1,
  "statuses": [
    {
      "code": 418,
      "name": "I'm a Teapot",
      "spec": "RFC 2324",
      "description": "An April Fools' joke from the Hyper Text Coffee Pot Control Protocol, kept alive by tradition."
    }
  ]
}
GET /v1/currencies/{code}

Look up one currency by ISO 4217 code

The single-currency form of /v1/currencies, plus the countries that use it. Minor units is the field worth reading: it is how many decimal places the currency has, and assuming two is a well-worn way to lose money in JPY or KWD.

ParameterTypeDescriptionValue
code path required string ISO 4217 three-letter code
Example response
{
  "code": "SEK",
  "minor_units": 2,
  "name": "Swedish Krona",
  "numeric": "752",
  "smallest_unit": 0.01,
  "symbol": "kr",
  "used_by": [
    {
      "flag": "🇸🇪",
      "iso2": "SE",
      "name": "Sweden"
    }
  ],
  "used_by_count": 1
}
GET /v1/emoji

Search emoji by name

The full Unicode emoji list with names, groups and codepoints. Leave the query empty and set random=true for a random pick.

ParameterTypeDescriptionValue
q string Match on name, group or subgroup
group string Restrict to one group
random boolean Return a random selection instead of the first matches
limit integer Maximum results, 1-200 default 20
Example response
{
  "count": 1,
  "emoji": [
    {
      "emoji": "🚀",
      "name": "rocket",
      "group": "Travel \u0026 Places",
      "subgroup": "transport-air",
      "codepoints": "U+1F680"
    }
  ],
  "total": 3944
}

Meta

GET /v1/health

Liveness check

Always returns 200 while the process is serving. Suitable as a load balancer or container health probe.

Example response
{
  "status": "ok"
}
GET /v1/version

Build information

Example response
{
  "build_date": "2026-08-07T20:00:00Z",
  "commit": "a1b2c3d",
  "version": "v0.1.0"
}
GET /v1/changelog

New endpoints and capabilities, newest first

A record of what you can newly do with the API, and of anything withdrawn. The same data renders the Changelog section of this page, so polling this endpoint is enough to learn about new endpoints, and to catch a removal before it breaks your code. Bug fixes and internal changes are not listed.

ParameterTypeDescriptionValue
limit integer Return only the newest N releases; 0 returns all default 0
type string Keep only changes of one type: added, changed or removed
since string Only releases on or after this date (YYYY-MM-DD)
version string Only the release published as this version
Example response
{
  "count": 1,
  "latest": "2026-08-09",
  "releases": [
    {
      "date": "2026-08-09",
      "version": "v1.0.0",
      "title": "Changelog",
      "changes": [
        {
          "type": "added",
          "text": "A changelog, so you can see when endpoints arrive.",
          "endpoints": [
            "/v1/changelog"
          ]
        }
      ]
    }
  ],
  "total": 3
}