Skip to main content

Unicode Normalization and Segmentation API

REAL DATA

Inspect UTF-16 and Unicode scalar values, apply the four standard normalization forms, and segment text into graphemes, words or sentences.

Authoritative reference data or standards computation

Base URL
/api/unicode
Capabilities
3 routes
Last updated
July 30, 2026
Data source

GET /api/unicode/inspect

Live requestRuns against the public API
No key required
GET/api/unicode/inspect?text=e%CC%81+%F0%9F%91%8B

Request parameters

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/unicode/inspect?text=e%CC%81%20%F0%9F%91%8B"
const res = await fetch("https://randomapi.dev/api/unicode/inspect?text=e%CC%81%20%F0%9F%91%8B");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/unicode/inspect?text=e%CC%81%20%F0%9F%91%8B").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/unicode/inspect?text=e%CC%81%20%F0%9F%91%8B"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 2
text string

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

allowed: 1 – 512
example: text=Café 👩‍💻
utf16 string

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

allowed: 4 – 2559
example: utf16=0041,D83D,DC4B
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=input,source
exclude list

Return all fields except these (comma-separated).

example: exclude=codePoints
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 7
input string

Resolved input string, including any unpaired surrogate code units.

example: A👋

source string

Input parameter used: text or utf16.

example: utf16

codeUnitLength integer

Number of UTF-16 code units in input.

example: 3

scalarValueCount integer

Number of Unicode scalar values; unpaired surrogates are excluded.

example: 2

unpairedSurrogateCount integer

Number of isolated UTF-16 high or low surrogate code units.

example: 0

wellFormed boolean

True when input contains no unpaired UTF-16 surrogates.

example: true

codePoints object[]

Ordered scalar values or isolated surrogates with code-point notation and UTF-16 offsets.

example: [{"character":"👋","codePoint":"U+1F44B","decimal":128075,"utf16":["D83D","DC4B"],"codeUnitStart":1,"codeUnitLength":2,"isScalar":true}]

Documented examples

Build-generated requests and complete responses
2
Inspect a combining sequence and emoji
GET /api/unicode/inspect?text=e%CC%81%20%F0%9F%91%8B
{
  "data": {
    "input": "é 👋",
    "source": "text",
    "codeUnitLength": 5,
    "scalarValueCount": 4,
    "unpairedSurrogateCount": 0,
    "wellFormed": true,
    "codePoints": [
      {
        "character": "e",
        "codePoint": "U+0065",
        "decimal": 101,
        "utf16": [
          "0065"
        ],
        "codeUnitStart": 0,
        "codeUnitLength": 1,
        "isScalar": true
      },
      {
        "character": "́",
        "codePoint": "U+0301",
        "decimal": 769,
        "utf16": [
          "0301"
        ],
        "codeUnitStart": 1,
        "codeUnitLength": 1,
        "isScalar": true
      },
      {
        "character": " ",
        "codePoint": "U+0020",
        "decimal": 32,
        "utf16": [
          "0020"
        ],
        "codeUnitStart": 2,
        "codeUnitLength": 1,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Expose an unpaired high surrogate
GET /api/unicode/inspect?utf16=0041%2CD800%2C0042
{
  "data": {
    "input": "A\ud800B",
    "source": "utf16",
    "codeUnitLength": 3,
    "scalarValueCount": 2,
    "unpairedSurrogateCount": 1,
    "wellFormed": false,
    "codePoints": [
      {
        "character": "A",
        "codePoint": "U+0041",
        "decimal": 65,
        "utf16": [
          "0041"
        ],
        "codeUnitStart": 0,
        "codeUnitLength": 1,
        "isScalar": true
      },
      {
        "character": "\ud800",
        "codePoint": "U+D800",
        "decimal": 55296,
        "utf16": [
          "D800"
        ],
        "codeUnitStart": 1,
        "codeUnitLength": 1,
        "isScalar": false
      },
      {
        "character": "B",
        "codePoint": "U+0042",
        "decimal": 66,
        "utf16": [
          "0042"
        ],
        "codeUnitStart": 2,
        "codeUnitLength": 1,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/unicode/normalize

Live requestRuns against the public API
No key required
GET/api/unicode/normalize?text=e%CC%81&form=NFC

Request parameters

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

Unicode normalization form to apply.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/unicode/normalize?text=e%CC%81&form=NFC"
const res = await fetch("https://randomapi.dev/api/unicode/normalize?text=e%CC%81&form=NFC");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/unicode/normalize?text=e%CC%81&form=NFC").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/unicode/normalize?text=e%CC%81&form=NFC"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 3
text string

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

allowed: 1 – 512
example: text=Café 👩‍💻
utf16 string

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

allowed: 4 – 2559
example: utf16=0041,D83D,DC4B
form enum

Unicode normalization form to apply.

default: NFC
allowed: NFC | NFD | NFKC | NFKD
example: form=NFD
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=input,source
exclude list

Return all fields except these (comma-separated).

example: exclude=wellFormed
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 12
input string

Resolved original string.

example: é

source string

Input parameter used: text or utf16.

example: text

form string

Applied normalization form.

example: NFC

normalized string

Normalized output string.

example: é

changed boolean

Whether normalization changed the UTF-16 sequence.

example: true

inputCodeUnits integer

Original UTF-16 code-unit length.

example: 2

outputCodeUnits integer

Normalized UTF-16 code-unit length.

example: 1

inputScalarValues integer

Original Unicode scalar-value count.

example: 2

outputScalarValues integer

Normalized Unicode scalar-value count.

example: 1

inputUnpairedSurrogates integer

Original isolated-surrogate count.

example: 0

outputUnpairedSurrogates integer

Normalized isolated-surrogate count.

example: 0

wellFormed boolean

Whether both input and output contain no unpaired surrogates.

example: true

Documented examples

Build-generated requests and complete responses
2
Compose e plus acute accent as NFC
GET /api/unicode/normalize?text=e%CC%81&form=NFC
{
  "data": {
    "input": "é",
    "source": "text",
    "form": "NFC",
    "normalized": "é",
    "changed": true,
    "inputCodeUnits": 2,
    "outputCodeUnits": 1,
    "inputScalarValues": 2,
    "outputScalarValues": 1,
    "inputUnpairedSurrogates": 0,
    "outputUnpairedSurrogates": 0,
    "wellFormed": true
  },
  "meta": {
    "endpoint": "unicode",
    "route": "/normalize",
    "params": {
      "text": "é",
      "form": "NFC"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Compatibility-fold full-width letters
GET /api/unicode/normalize?text=%EF%BC%B2%EF%BD%81%EF%BD%8E%EF%BD%84%EF%BD%8F%EF%BD%8D&form=NFKC
{
  "data": {
    "input": "Random",
    "source": "text",
    "form": "NFKC",
    "normalized": "Random",
    "changed": true,
    "inputCodeUnits": 6,
    "outputCodeUnits": 6,
    "inputScalarValues": 6,
    "outputScalarValues": 6,
    "inputUnpairedSurrogates": 0,
    "outputUnpairedSurrogates": 0,
    "wellFormed": true
  },
  "meta": {
    "endpoint": "unicode",
    "route": "/normalize",
    "params": {
      "text": "Random",
      "form": "NFKC"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/unicode/segment

Live requestRuns against the public API
No key required
GET/api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme

Request parameters

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

Boundary type used by Intl.Segmenter.

Requested locale used for locale-sensitive boundary rules.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme"
const res = await fetch("https://randomapi.dev/api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
text string

Unicode text to process, limited to 512 UTF-16 code units. Use exactly one of text or utf16.

allowed: 1 – 512
example: text=Café 👩‍💻
utf16 string

Comma-separated four-digit hexadecimal UTF-16 code units; this form can represent unpaired surrogates. Use exactly one input.

allowed: 4 – 2559
example: utf16=0041,D83D,DC4B
granularity enum

Boundary type used by Intl.Segmenter.

default: grapheme
allowed: grapheme | word | sentence
example: granularity=word
language enum

Requested locale used for locale-sensitive boundary rules.

default: en
allowed: en | da | de | fr | es | it | pt | ja
example: language=da
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=input,source
exclude list

Return all fields except these (comma-separated).

example: exclude=segments
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 7
input string

Resolved original string.

example: Hello world!

source string

Input parameter used: text or utf16.

example: text

granularity string

Requested segmentation granularity.

example: word

requestedLocale string

Locale requested through language.

example: en

resolvedLocale string

Locale selected by the JavaScript runtime.

example: en

count integer

Number of returned segments.

example: 4

segments object[]

Segments with UTF-16 start offsets and word-likeness where applicable.

example: [{"segment":"Hello","index":0,"isWordLike":true}]

Documented examples

Build-generated requests and complete responses
2
Keep a family emoji as one grapheme
GET /api/unicode/segment?text=%F0%9F%91%A8%E2%80%8D%F0%9F%91%A9%E2%80%8D%F0%9F%91%A7%E2%80%8D%F0%9F%91%A6%21&granularity=grapheme
{
  "data": {
    "input": "👨‍👩‍👧‍👦!",
    "source": "text",
    "granularity": "grapheme",
    "requestedLocale": "en",
    "resolvedLocale": "en",
    "count": 2,
    "segments": [
      {
        "segment": "👨‍👩‍👧‍👦",
        "index": 0,
        "isWordLike": null
      },
      {
        "segment": "!",
        "index": 11,
        "isWordLike": null
      }
    ]
  },
  "meta": {
    "endpoint": "unicode",
    "route": "/segment",
    "params": {
      "text": "👨‍👩‍👧‍👦!",
      "granularity": "grapheme",
      "language": "en"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Find Danish word boundaries
GET /api/unicode/segment?text=Hej%20verden%21&granularity=word&language=da
{
  "data": {
    "input": "Hej verden!",
    "source": "text",
    "granularity": "word",
    "requestedLocale": "da",
    "resolvedLocale": "da",
    "count": 4,
    "segments": [
      {
        "segment": "Hej",
        "index": 0,
        "isWordLike": true
      },
      {
        "segment": " ",
        "index": 3,
        "isWordLike": false
      },
      {
        "segment": "verden",
        "index": 4,
        "isWordLike": true
      },
      {
        "segment": "!",
        "index": 10,
        "isWordLike": false
      }
    ]
  },
  "meta": {
    "endpoint": "unicode",
    "route": "/segment",
    "params": {
      "text": "Hej verden!",
      "granularity": "word",
      "language": "da"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

About this API

Coverage & behavior

Bounded Unicode tools backed by the JavaScript runtime's standards-defined string, normalization and Intl.Segmenter behavior. /inspect walks UTF-16 without hiding malformed input: surrogate pairs become one scalar value, while isolated high or low surrogates remain visible with isScalar=false and make wellFormed=false.

/normalize applies NFC, NFD, NFKC or NFKD as specified by Unicode Standard Annex #15 and reports whether the string changed. Canonical forms compose or decompose canonically equivalent sequences; compatibility forms can also fold presentation distinctions. Normalization is not transliteration, case folding or sanitization.

/segment uses Intl.Segmenter, whose grapheme, word and sentence boundaries follow Unicode text segmentation rules together with the selected locale. Segment indexes are UTF-16 code-unit offsets, matching JavaScript string indexing. Use the hexadecimal utf16 input when a test must include an unpaired surrogate that cannot safely travel as ordinary URL text.

Use it for

  • Compare composed and decomposed Unicode fixture strings
  • Audit emoji, surrogate pairs and malformed UTF-16 with exact offsets
  • Split localized text at grapheme, word or sentence boundaries

Frequently asked questions

What is the difference between code points and UTF-16 code units?

A BMP scalar uses one UTF-16 code unit, while a supplementary scalar such as many emoji uses a two-unit surrogate pair. An isolated surrogate is a code unit but not a Unicode scalar value.

Which Unicode normalization forms are supported?

NFC, NFD, NFKC and NFKD are supported through the runtime's standards-defined String.normalize implementation.

Will grapheme segmentation keep joined emoji together?

Intl.Segmenter applies Unicode grapheme-boundary rules, so sequences such as flags, skin-tone emoji and many zero-width-joiner emoji are treated as user-perceived units.

Are segment indexes bytes or characters?

They are zero-based UTF-16 code-unit offsets, the same indexing model used by JavaScript strings.

Standards & references