group enum Only emoji in this top-level Unicode group, as a URL-safe slug (smileys-emotion, people-body, animals-nature, food-drink, travel-places, activities, objects, symbols, flags). Records return Unicode's official group name.
All 3944 fully-qualified Unicode 17.0 emoji with groups, CLDR keywords and code points — plus grapheme-correct emoji counting for any text.
Authoritative reference data or standards computation
Seeded sample of the fully-qualified set. Filters narrow the pool; seed fixes the order forever.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
group | enum | default: any allowed: smileys-emotion | people-body | animals-nature | food-drink | travel-places | activities | objects | symbols | flags | any example: food-drink | Only emoji in this top-level Unicode group, as a URL-safe slug (smileys-emotion, people-body, animals-nature, food-drink, travel-places, activities, objects, symbols, flags). Records return Unicode's official group name. |
subgroup | string | allowed: ≤ 40 example: animal-mammal | Only emoji in this Unicode subgroup, e.g. face-smiling. 98 values; GET /api/emoji/groups lists them all. An unknown subgroup is a 400, never a silent empty list. |
version | enum | default: any allowed: 0.6 | 0.7 | 1.0 | 2.0 | 3.0 | 4.0 | 5.0 | 11.0 | 12.0 | 12.1 | 13.0 | 13.1 | 14.0 | 15.0 | 15.1 | 16.0 | 17.0 | any example: 17.0 | Only emoji introduced in exactly this emoji release. |
maxVersion | enum | allowed: 0.6 | 0.7 | 1.0 | 2.0 | 3.0 | 4.0 | 5.0 | 11.0 | 12.0 | 12.1 | 13.0 | 13.1 | 14.0 | 15.0 | 15.1 | 16.0 | 17.0 example: 12.0 | Only emoji introduced at or before this emoji release — the practical filter for older client fonts. |
skinTones | boolean | default: false example: true | Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944. |
zwj | boolean | default: true example: false | Include zero-width-joiner sequences (families, professions, flags such as 🏳️🌈). Set false for single-emoji sequences only. |
group enum Only emoji in this top-level Unicode group, as a URL-safe slug (smileys-emotion, people-body, animals-nature, food-drink, travel-places, activities, objects, symbols, flags). Records return Unicode's official group name.
subgroup string Only emoji in this Unicode subgroup, e.g. face-smiling. 98 values; GET /api/emoji/groups lists them all. An unknown subgroup is a 400, never a silent empty list.
version enum Only emoji introduced in exactly this emoji release.
maxVersion enum Only emoji introduced at or before this emoji release — the practical filter for older client fonts.
skinTones boolean Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944.
zwj boolean Include zero-width-joiner sequences (families, professions, flags such as 🏳️🌈). Set false for single-emoji sequences only.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
count | int | default: 20 allowed: 1 – 100 example: 3 | Limit the number of returned records (1–100). Defaults to 20. |
seed | int | example: 42 | Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed). |
fields | list | example: emoji,name | Return only these fields (comma-separated). Mutually exclusive with 'exclude'. |
exclude | list | example: keywords | Return all fields except these (comma-separated). |
format | enum | default: json allowed: json | ndjson | csv example: csv | Response format: json envelope, ndjson (one record per line) or csv. |
pretty | boolean | default: false example: true | Pretty-print the JSON response. |
unwrap | boolean | default: false example: true | Drop the envelope: return the raw array/object without data/meta wrapper. |
count int Limit the number of returned records (1–100). Defaults to 20.
seed int Deterministic output: the same seed always returns the same records. Omit for random (the used seed is echoed in meta.seed).
fields list Return only these fields (comma-separated). Mutually exclusive with 'exclude'.
exclude list Return all fields except these (comma-separated).
format enum Response format: json envelope, ndjson (one record per line) or csv.
pretty boolean Pretty-print the JSON response.
unwrap boolean Drop the envelope: return the raw array/object without data/meta wrapper.
| Field | Type | Description | Example |
|---|---|---|---|
emoji | string | The fully-qualified sequence itself, U+FE0F included where the sequence needs it. | 👩💻 |
name | string | Unicode's name for the sequence, from emoji-test.txt. | woman technologist |
slug | string | URL-safe form of the name; unique across all sequences and accepted by /lookup. | woman-technologist |
codePoints | string[] | Every code point of the sequence in U+ notation, in order. | ["U+1F469","U+200D","U+1F4BB"] |
group | string | Unicode's top-level category, e.g. Smileys & Emotion. | People & Body |
subgroup | string | Unicode's subcategory, e.g. face-smiling. | person-role |
unicodeVersion | string | Emoji release that introduced the sequence — clients with older fonts will show tofu for newer releases. | 4.0 |
hasSkinTone | boolean | True when the sequence carries a U+1F3FB–U+1F3FF skin-tone modifier. | false |
isZwjSequence | boolean | True when several emoji are joined with U+200D ZERO WIDTH JOINER. | true |
keywords | string[] | CLDR English search keywords, in CLDR's order — what /search matches on besides the name. | ["computer","developer","software","technologist","woman"] |
emoji string The fully-qualified sequence itself, U+FE0F included where the sequence needs it.
example: 👩💻
name string Unicode's name for the sequence, from emoji-test.txt.
example: woman technologist
slug string URL-safe form of the name; unique across all sequences and accepted by /lookup.
example: woman-technologist
codePoints string[] Every code point of the sequence in U+ notation, in order.
example: ["U+1F469","U+200D","U+1F4BB"]
group string Unicode's top-level category, e.g. Smileys & Emotion.
example: People & Body
subgroup string Unicode's subcategory, e.g. face-smiling.
example: person-role
unicodeVersion string Emoji release that introduced the sequence — clients with older fonts will show tofu for newer releases.
example: 4.0
hasSkinTone boolean True when the sequence carries a U+1F3FB–U+1F3FF skin-tone modifier.
example: false
isZwjSequence boolean True when several emoji are joined with U+200D ZERO WIDTH JOINER.
example: true
keywords string[] CLDR English search keywords, in CLDR's order — what /search matches on besides the name.
example: ["computer","developer","software","technologist","woman"]
/api/emoji {
"data": [
{
"emoji": "🤷♀️",
"name": "woman shrugging",
"slug": "woman-shrugging",
"codePoints": [
"U+1F937",
"U+200D",
"U+2640",
"U+FE0F"
],
"group": "People & Body",
"subgroup": "person-gesture",
"unicodeVersion": "4.0",
"hasSkinTone": false,
"isZwjSequence": true,
"keywords": [
"doubt",
"dunno",
"guess",
"idk",
"ignorance",
"indifference",
"knows",
"maybe",
"shrug",
"shrugging",
"whatever",
"who",
"woman"
]
},
{
"emoji": "🆘",
"name": "SOS button",
"slug": "sos-button",
"codePoints": [
"U+1F198"
],
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji?group=food-drink&count=5&seed=42 {
"data": [
{
"emoji": "🥙",
"name": "stuffed flatbread",
"slug": "stuffed-flatbread",
"codePoints": [
"U+1F959"
],
"group": "Food & Drink",
"subgroup": "food-prepared",
"unicodeVersion": "3.0",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"falafel",
"flatbread",
"food",
"gyro",
"kebab",
"stuffed"
]
},
{
"emoji": "🧅",
"name": "onion",
"slug": "onion",
"codePoints": [
"U+1F9C5"
],
"group": "Food & Drink",
"subgroup": "food-vegetable",
"unicodeVersion": "12.0",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"flavoring",
"onion"
]
},
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji?maxVersion=5.0&zwj=false&count=10 {
"data": [
{
"emoji": "🌥️",
"name": "sun behind large cloud",
"slug": "sun-behind-large-cloud",
"codePoints": [
"U+1F325",
"U+FE0F"
],
"group": "Travel & Places",
"subgroup": "sky & weather",
"unicodeVersion": "0.7",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"behind",
"cloud",
"sun",
"weather"
]
},
{
"emoji": "🇳🇫",
"name": "flag: Norfolk Island",
"slug": "flag-norfolk-island",
"codePoints": [
"U+1F1F3",
"U+1F1EB"
],
"group": "Flags",
"subgroup": "country-flag",
"unicodeVersion": "2.0",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"flag"
]
},
{
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji?version=17.0&skinTones=true&count=100 {
"data": [
{
"emoji": "🤼🏾♂️",
"name": "men wrestling: medium-dark skin tone",
"slug": "men-wrestling-medium-dark-skin-tone",
"codePoints": [
"U+1F93C",
"U+1F3FE",
"U+200D",
"U+2642",
"U+FE0F"
],
"group": "People & Body",
"subgroup": "person-sport",
"unicodeVersion": "17.0",
"hasSkinTone": true,
"isZwjSequence": true,
"keywords": [
"combat",
"duel",
"grapple",
"medium-dark skin tone",
"men",
"ring",
"tournament",
"wrestle",
"wrestling"
]
},
{
"emoji": "🤼🏼♂️",
"name": "men wrestling: medium-light skin tone",
"slug": "men-wrestling-medium-light-skin-tone",
"codePoints": [
"U+1F93C",
"U+1F3FC",
"U+200D",
"U+2642",
"U+FE0F"
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} | Parameter | Type | Default & allowed | Description |
|---|---|---|---|
emoji | string | allowed: ≤ 64 example: 👩💻 | The emoji sequence itself. U+FE0F is ignored when matching, so an unqualified spelling still resolves. |
slug | string | allowed: ≤ 80 example: woman-technologist | The URL-safe name, e.g. woman-technologist. |
codepoints | string | allowed: ≤ 120 example: 1F469+200D+1F4BB | 1–16 hexadecimal code points separated by spaces, commas, plus signs or hyphens, with or without U+ prefixes. |
emoji string The emoji sequence itself. U+FE0F is ignored when matching, so an unqualified spelling still resolves.
slug string The URL-safe name, e.g. woman-technologist.
codepoints string 1–16 hexadecimal code points separated by spaces, commas, plus signs or hyphens, with or without U+ prefixes.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
fields | list | example: emoji,name | Return only these fields (comma-separated). Mutually exclusive with 'exclude'. |
exclude | list | example: keywords | Return all fields except these (comma-separated). |
pretty | boolean | default: false example: true | Pretty-print the JSON response. |
unwrap | boolean | default: false example: true | Drop the envelope: return the raw array/object without data/meta wrapper. |
fields list Return only these fields (comma-separated). Mutually exclusive with 'exclude'.
exclude list Return all fields except these (comma-separated).
pretty boolean Pretty-print the JSON response.
unwrap boolean Drop the envelope: return the raw array/object without data/meta wrapper.
| Field | Type | Description | Example |
|---|---|---|---|
emoji | string | The fully-qualified sequence itself, U+FE0F included where the sequence needs it. | 👩💻 |
name | string | Unicode's name for the sequence, from emoji-test.txt. | woman technologist |
slug | string | URL-safe form of the name; unique across all sequences and accepted by /lookup. | woman-technologist |
codePoints | string[] | Every code point of the sequence in U+ notation, in order. | ["U+1F469","U+200D","U+1F4BB"] |
group | string | Unicode's top-level category, e.g. Smileys & Emotion. | People & Body |
subgroup | string | Unicode's subcategory, e.g. face-smiling. | person-role |
unicodeVersion | string | Emoji release that introduced the sequence — clients with older fonts will show tofu for newer releases. | 4.0 |
hasSkinTone | boolean | True when the sequence carries a U+1F3FB–U+1F3FF skin-tone modifier. | false |
isZwjSequence | boolean | True when several emoji are joined with U+200D ZERO WIDTH JOINER. | true |
keywords | string[] | CLDR English search keywords, in CLDR's order — what /search matches on besides the name. | ["computer","developer","software","technologist","woman"] |
emoji string The fully-qualified sequence itself, U+FE0F included where the sequence needs it.
example: 👩💻
name string Unicode's name for the sequence, from emoji-test.txt.
example: woman technologist
slug string URL-safe form of the name; unique across all sequences and accepted by /lookup.
example: woman-technologist
codePoints string[] Every code point of the sequence in U+ notation, in order.
example: ["U+1F469","U+200D","U+1F4BB"]
group string Unicode's top-level category, e.g. Smileys & Emotion.
example: People & Body
subgroup string Unicode's subcategory, e.g. face-smiling.
example: person-role
unicodeVersion string Emoji release that introduced the sequence — clients with older fonts will show tofu for newer releases.
example: 4.0
hasSkinTone boolean True when the sequence carries a U+1F3FB–U+1F3FF skin-tone modifier.
example: false
isZwjSequence boolean True when several emoji are joined with U+200D ZERO WIDTH JOINER.
example: true
keywords string[] CLDR English search keywords, in CLDR's order — what /search matches on besides the name.
example: ["computer","developer","software","technologist","woman"]
/api/emoji/lookup?emoji=%F0%9F%98%80 {
"data": {
"emoji": "😀",
"name": "grinning face",
"slug": "grinning-face",
"codePoints": [
"U+1F600"
],
"group": "Smileys & Emotion",
"subgroup": "face-smiling",
"unicodeVersion": "1.0",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"cheerful",
"cheery",
"face",
"grin",
"grinning",
"happy",
"laugh",
"nice",
"smile",
"smiling",
"teeth"
]
},
"meta": {
"endpoint": "emoji",
"route": "/lookup",
"params": {
"emoji": "😀"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/lookup?slug=grinning-face {
"data": {
"emoji": "😀",
"name": "grinning face",
"slug": "grinning-face",
"codePoints": [
"U+1F600"
],
"group": "Smileys & Emotion",
"subgroup": "face-smiling",
"unicodeVersion": "1.0",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"cheerful",
"cheery",
"face",
"grin",
"grinning",
"happy",
"laugh",
"nice",
"smile",
"smiling",
"teeth"
]
},
"meta": {
"endpoint": "emoji",
"route": "/lookup",
"params": {
"slug": "grinning-face"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/lookup?codepoints=1F469%2B200D%2B1F4BB {
"data": {
"emoji": "👩💻",
"name": "woman technologist",
"slug": "woman-technologist",
"codePoints": [
"U+1F469",
"U+200D",
"U+1F4BB"
],
"group": "People & Body",
"subgroup": "person-role",
"unicodeVersion": "4.0",
"hasSkinTone": false,
"isZwjSequence": true,
"keywords": [
"coder",
"computer",
"developer",
"inventor",
"software",
"technologist",
"woman"
]
},
"meta": {
"endpoint": "emoji",
"route": "/lookup",
"params": {
"codepoints": "1F469+200D+1F4BB"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/lookup?codepoints=263A {
"data": {
"emoji": "☺️",
"name": "smiling face",
"slug": "smiling-face",
"codePoints": [
"U+263A",
"U+FE0F"
],
"group": "Smileys & Emotion",
"subgroup": "face-affection",
"unicodeVersion": "0.6",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"face",
"happy",
"outlined",
"relaxed",
"smile",
"smiling"
]
},
"meta": {
"endpoint": "emoji",
"route": "/lookup",
"params": {
"codepoints": "263A"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} | Parameter | Type | Default & allowed | Description |
|---|---|---|---|
q
required
| string | allowed: 1 – 64 example: happy | Search text, matched case-insensitively. Substring matching applies to the Unicode name and to the CLDR English keywords; the slug matches only in full (`grinning-face`), because a slug's hyphens are not word boundaries in the name. |
limit | int | default: 20 allowed: 1 – 100 example: 5 | Maximum number of ranked matches to return. |
skinTones | boolean | default: false example: true | Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944. |
q string required Search text, matched case-insensitively. Substring matching applies to the Unicode name and to the CLDR English keywords; the slug matches only in full (`grinning-face`), because a slug's hyphens are not word boundaries in the name.
limit int Maximum number of ranked matches to return.
skinTones boolean Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
fields | list | example: query,matchCount | Return only these fields (comma-separated). Mutually exclusive with 'exclude'. |
exclude | list | example: matches | Return all fields except these (comma-separated). |
pretty | boolean | default: false example: true | Pretty-print the JSON response. |
unwrap | boolean | default: false example: true | Drop the envelope: return the raw array/object without data/meta wrapper. |
fields list Return only these fields (comma-separated). Mutually exclusive with 'exclude'.
exclude list Return all fields except these (comma-separated).
pretty boolean Pretty-print the JSON response.
unwrap boolean Drop the envelope: return the raw array/object without data/meta wrapper.
| Field | Type | Description | Example |
|---|---|---|---|
query | string | The normalized (lower-cased, trimmed) search text. | happy |
matchCount | integer | How many emoji matched in total, before `limit` is applied. | 27 |
returned | integer | How many matches are in `matches`. | 5 |
matches | object[] | Ranked matches, best first: exact name or slug, then name prefix, then name substring, then exact keyword, then keyword prefix, then keyword substring. Each entry carries the full emoji record plus `matchedOn`, which is `name` for the first three ranks and `keyword` for the last three. Ties break on the shorter name, then alphabetically. | [{"emoji":"😀","name":"grinning face","slug":"grinning-face","codePoints":["U+1F600"],"group":"Smileys & Emotion","subgroup":"face-smiling","unicodeVersion":"1.0","hasSkinTone":false,"isZwjSequence":false,"keywords":["face","grin","grinning face"],"matchedOn":"keyword"}] |
query string The normalized (lower-cased, trimmed) search text.
example: happy
matchCount integer How many emoji matched in total, before `limit` is applied.
example: 27
returned integer How many matches are in `matches`.
example: 5
matches object[] Ranked matches, best first: exact name or slug, then name prefix, then name substring, then exact keyword, then keyword prefix, then keyword substring. Each entry carries the full emoji record plus `matchedOn`, which is `name` for the first three ranks and `keyword` for the last three. Ties break on the shorter name, then alphabetically.
example: [{"emoji":"😀","name":"grinning face","slug":"grinning-face","codePoints":["U+1F600"],"group":"Smileys & Emotion","subgroup":"face-smiling","unicodeVersion":"1.0","hasSkinTone":false,"isZwjSequence":false,"keywords":["face","grin","grinning face"],"matchedOn":"keyword"}]
/api/emoji/search?q=happy {
"data": {
"query": "happy",
"matchCount": 27,
"returned": 20,
"matches": [
{
"emoji": "☺️",
"name": "smiling face",
"slug": "smiling-face",
"codePoints": [
"U+263A",
"U+FE0F"
],
"group": "Smileys & Emotion",
"subgroup": "face-affection",
"unicodeVersion": "0.6",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"face",
"happy",
"outlined",
"relaxed",
"smile",
"smiling"
],
"matchedOn": "keyword"
},
{
"emoji": "🎂",
"name": "birthday cake",
"slug": "birthday-cake",
"codePoints": [
"U+1F382"
],
"group": "Food & Drink",
"subgroup": "food-sweet",
"unicodeVersion": "0.6",
"hasSkinTone": false,
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/search?q=cat&limit=5 {
"data": {
"query": "cat",
"matchCount": 51,
"returned": 5,
"matches": [
{
"emoji": "🐈",
"name": "cat",
"slug": "cat",
"codePoints": [
"U+1F408"
],
"group": "Animals & Nature",
"subgroup": "animal-mammal",
"unicodeVersion": "0.7",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"animal",
"animals",
"cat",
"cats",
"kitten",
"pet"
],
"matchedOn": "name"
},
{
"emoji": "🐱",
"name": "cat face",
"slug": "cat-face",
"codePoints": [
"U+1F431"
],
"group": "Animals & Nature",
"subgroup": "animal-mammal",
"unicodeVersion": "0.6",
"hasSkinTone": false,
"isZwjSequence": false,
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/search?q=wave&skinTones=true&limit=10 {
"data": {
"query": "wave",
"matchCount": 25,
"returned": 10,
"matches": [
{
"emoji": "🌊",
"name": "water wave",
"slug": "water-wave",
"codePoints": [
"U+1F30A"
],
"group": "Travel & Places",
"subgroup": "sky & weather",
"unicodeVersion": "0.6",
"hasSkinTone": false,
"isZwjSequence": false,
"keywords": [
"nature",
"ocean",
"surf",
"surfer",
"surfing",
"water",
"wave"
],
"matchedOn": "name"
},
{
"emoji": "👋",
"name": "waving hand",
"slug": "waving-hand",
"codePoints": [
"U+1F44B"
],
"group": "People & Body",
"subgroup": "hand-fingers-open",
"unicodeVersion": "0.6",
"hasSkinTone": false,
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} | Parameter | Type | Default & allowed | Description |
|---|---|---|---|
skinTones | boolean | default: false example: true | Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944. |
skinTones boolean Include the 2030 skin-tone variants. Off by default, which leaves the 1914 distinct emoji; on gives all 3944.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
fields | list | example: emojiVersion,emojiCount | Return only these fields (comma-separated). Mutually exclusive with 'exclude'. |
exclude | list | example: groups | Return all fields except these (comma-separated). |
pretty | boolean | default: false example: true | Pretty-print the JSON response. |
unwrap | boolean | default: false example: true | Drop the envelope: return the raw array/object without data/meta wrapper. |
fields list Return only these fields (comma-separated). Mutually exclusive with 'exclude'.
exclude list Return all fields except these (comma-separated).
pretty boolean Pretty-print the JSON response.
unwrap boolean Drop the envelope: return the raw array/object without data/meta wrapper.
| Field | Type | Description | Example |
|---|---|---|---|
emojiVersion | string | The pinned Unicode Emoji release these counts describe. | 17.0 |
emojiCount | integer | Total emoji counted under the current `skinTones` setting. | 1914 |
groupCount | integer | Number of top-level groups. | 9 |
subgroupCount | integer | Number of subgroups across all groups. | 98 |
groups | object[] | Groups in Unicode's own order, each with its emoji count and its subgroups (name, count and one sample emoji). | [{"group":"Flags","slug":"flags","count":270,"subgroups":[{"subgroup":"flag","count":8,"sample":"🏁"}]}] |
emojiVersion string The pinned Unicode Emoji release these counts describe.
example: 17.0
emojiCount integer Total emoji counted under the current `skinTones` setting.
example: 1914
groupCount integer Number of top-level groups.
example: 9
subgroupCount integer Number of subgroups across all groups.
example: 98
groups object[] Groups in Unicode's own order, each with its emoji count and its subgroups (name, count and one sample emoji).
example: [{"group":"Flags","slug":"flags","count":270,"subgroups":[{"subgroup":"flag","count":8,"sample":"🏁"}]}]
/api/emoji/groups {
"data": {
"emojiVersion": "17.0",
"emojiCount": 1914,
"groupCount": 9,
"subgroupCount": 98,
"groups": [
{
"group": "Smileys & Emotion",
"slug": "smileys-emotion",
"count": 171,
"subgroups": [
{
"subgroup": "face-smiling",
"count": 14,
"sample": "😀"
},
{
"subgroup": "face-affection",
"count": 9,
"sample": "🥰"
},
{
"subgroup": "face-tongue",
"count": 6,
"sample": "😋"
},
{
"subgroup": "face-hand",
"count": 7,
"sample": "🤗"
},
{
"subgroup": "face-neutral-skeptical",
"count": 16,
"sample": "🤐"
},
{
"subgroup": "face-sleepy",
"count": 6,
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/groups?skinTones=true {
"data": {
"emojiVersion": "17.0",
"emojiCount": 3944,
"groupCount": 9,
"subgroupCount": 98,
"groups": [
{
"group": "Smileys & Emotion",
"slug": "smileys-emotion",
"count": 171,
"subgroups": [
{
"subgroup": "face-smiling",
"count": 14,
"sample": "😀"
},
{
"subgroup": "face-affection",
"count": 9,
"sample": "🥰"
},
{
"subgroup": "face-tongue",
"count": 6,
"sample": "😋"
},
{
"subgroup": "face-hand",
"count": 7,
"sample": "🤗"
},
{
"subgroup": "face-neutral-skeptical",
"count": 16,
"sample": "🤐"
},
{
"subgroup": "face-sleepy",
"count": 6,
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} | Parameter | Type | Default & allowed | Description |
|---|---|---|---|
text
required
| string | allowed: 1 – 1000 example: Shipping it 🚀 with the family 👨👩👧👦 | Text to scan, up to 1000 UTF-16 code units. Segmented with Intl.Segmenter, so one ZWJ family is one emoji. |
text string required Text to scan, up to 1000 UTF-16 code units. Segmented with Intl.Segmenter, so one ZWJ family is one emoji.
| Parameter | Type | Default & allowed | Description |
|---|---|---|---|
fields | list | example: text,graphemeCount | Return only these fields (comma-separated). Mutually exclusive with 'exclude'. |
exclude | list | example: emoji | Return all fields except these (comma-separated). |
pretty | boolean | default: false example: true | Pretty-print the JSON response. |
unwrap | boolean | default: false example: true | Drop the envelope: return the raw array/object without data/meta wrapper. |
fields list Return only these fields (comma-separated). Mutually exclusive with 'exclude'.
exclude list Return all fields except these (comma-separated).
pretty boolean Pretty-print the JSON response.
unwrap boolean Drop the envelope: return the raw array/object without data/meta wrapper.
| Field | Type | Description | Example |
|---|---|---|---|
text | string | The input, echoed exactly as received. | hi 👋🏽 and 👨👩👧👦 |
graphemeCount | integer | Total Unicode grapheme clusters in the input — what a user perceives as characters. | 10 |
emojiCount | integer | Total emoji occurrences; a ZWJ family, a flag and a skin-tone sequence each count as one. | 2 |
distinctEmojiCount | integer | Number of distinct emoji sequences found. | 2 |
textWithoutEmoji | string | The input with every emoji grapheme removed and nothing else changed. | hi and |
emoji | object[] | Each distinct emoji in first-appearance order, with its occurrence count and — when the sequence is in the pinned snapshot — its name, slug, group and release. `recognized` is false when the grapheme is emoji-shaped but is not a fully-qualified sequence of this release: an invalid regional-indicator pair such as 🇦🇦, a bare hair or component modifier, or a code point Unicode has reserved for a future emoji. Those still count, with `name`, `slug`, `group` and `unicodeVersion` null. | [{"emoji":"👋🏽","count":1,"recognized":true,"name":"waving hand: medium skin tone","slug":"waving-hand-medium-skin-tone","codePoints":["U+1F44B","U+1F3FD"],"group":"People & Body","unicodeVersion":"1.0"}] |
text string The input, echoed exactly as received.
example: hi 👋🏽 and 👨👩👧👦
graphemeCount integer Total Unicode grapheme clusters in the input — what a user perceives as characters.
example: 10
emojiCount integer Total emoji occurrences; a ZWJ family, a flag and a skin-tone sequence each count as one.
example: 2
distinctEmojiCount integer Number of distinct emoji sequences found.
example: 2
textWithoutEmoji string The input with every emoji grapheme removed and nothing else changed.
example: hi and
emoji object[] Each distinct emoji in first-appearance order, with its occurrence count and — when the sequence is in the pinned snapshot — its name, slug, group and release. `recognized` is false when the grapheme is emoji-shaped but is not a fully-qualified sequence of this release: an invalid regional-indicator pair such as 🇦🇦, a bare hair or component modifier, or a code point Unicode has reserved for a future emoji. Those still count, with `name`, `slug`, `group` and `unicodeVersion` null.
example: [{"emoji":"👋🏽","count":1,"recognized":true,"name":"waving hand: medium skin tone","slug":"waving-hand-medium-skin-tone","codePoints":["U+1F44B","U+1F3FD"],"group":"People & Body","unicodeVersion":"1.0"}]
/api/emoji/parse?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%20is%20one {
"data": {
"text": "👨👩👧👦 is one",
"graphemeCount": 8,
"emojiCount": 1,
"distinctEmojiCount": 1,
"textWithoutEmoji": " is one",
"emoji": [
{
"emoji": "👨👩👧👦",
"count": 1,
"recognized": true,
"name": "family: man, woman, girl, boy",
"slug": "family-man-woman-girl-boy",
"codePoints": [
"U+1F468",
"U+200D",
"U+1F469",
"U+200D",
"U+1F467",
"U+200D",
"U+1F466"
],
"group": "People & Body",
"unicodeVersion": "2.0"
}
]
},
"meta": {
"endpoint": "emoji",
"route": "/parse",
"params": {
"text": "👨👩👧👦 is one"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/parse?text=Hi%20%F0%9F%87%AC%F0%9F%87%A7%20%F0%9F%91%8B%F0%9F%8F%BD {
"data": {
"text": "Hi 🇬🇧 👋🏽",
"graphemeCount": 6,
"emojiCount": 2,
"distinctEmojiCount": 2,
"textWithoutEmoji": "Hi ",
"emoji": [
{
"emoji": "🇬🇧",
"count": 1,
"recognized": true,
"name": "flag: United Kingdom",
"slug": "flag-united-kingdom",
"codePoints": [
"U+1F1EC",
"U+1F1E7"
],
"group": "Flags",
"unicodeVersion": "0.6"
},
{
"emoji": "👋🏽",
"count": 1,
"recognized": true,
"name": "waving hand: medium skin tone",
"slug": "waving-hand-medium-skin-tone",
"codePoints": [
"U+1F44B",
"U+1F3FD"
],
"group": "People & Body",
"unicodeVersion": "1.0"
}
]
},
"meta": {
"endpoint": "emoji",
"route": "/parse",
"params": {
…
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} /api/emoji/parse?text=clean%20%F0%9F%A7%BC%20this%20%F0%9F%9A%80%20up {
"data": {
"text": "clean 🧼 this 🚀 up",
"graphemeCount": 17,
"emojiCount": 2,
"distinctEmojiCount": 2,
"textWithoutEmoji": "clean this up",
"emoji": [
{
"emoji": "🧼",
"count": 1,
"recognized": true,
"name": "soap",
"slug": "soap",
"codePoints": [
"U+1F9FC"
],
"group": "Objects",
"unicodeVersion": "11.0"
},
{
"emoji": "🚀",
"count": 1,
"recognized": true,
"name": "rocket",
"slug": "rocket",
"codePoints": [
"U+1F680"
],
"group": "Travel & Places",
"unicodeVersion": "0.6"
}
]
},
"meta": {
"endpoint": "emoji",
"route": "/parse",
"params": {
"text": "clean 🧼 this 🚀 up"
},
"generatedAt": "2026-09-15T19:47:18.000Z"
}
} Unicode's own emoji-test.txt for Emoji 17.0, pinned by SHA-256, joined to the CLDR English annotations (release-48-2) so every sequence carries real search keywords rather than a guess from its name.
The count nobody agrees on. There are 3944 fully-qualified emoji, but 2030 of those are skin-tone variants of an emoji already in the set. Exclude them and 1914 distinct emoji remain — which is why skinTones is an explicit switch that defaults to off, and why both numbers are printed here instead of one marketing figure.
/parse uses Intl.Segmenter grapheme segmentation, so 👨👩👧👦 counts as one emoji, not four. Regex-over-code-units emoji counters get this wrong on every ZWJ family, every flag and every skin-tone sequence.
/search matches the Unicode name and the CLDR keywords, so q=happy finds 😀 even though "happy" is not in its name. /groups returns Unicode's official 9-group, 98-subgroup tree with live counts, and /lookup resolves a sequence, a slug or a code-point list — an unqualified spelling such as 263A resolves to the fully-qualified ☺️ because lookups match on the U+FE0F-stripped key.
Rendering is your font's job, not the API's. unicodeVersion is the emoji release that introduced a sequence: anything from a recent release will render as tofu on a device whose emoji font predates it. Filter with maxVersion when you need a set that older clients can actually display.
Scope: only fully-qualified sequences are served. The minimally-qualified and unqualified rows upstream are alternative U+FE0F spellings of sequences already present, and the nine component rows (bare skin-tone and hair modifiers) are not standalone emoji.
Unicode Emoji 17.0 has 3944 fully-qualified sequences, but 2030 of them are skin-tone variants of another sequence. Excluding those leaves 1914 distinct emoji, which is what this API returns unless you pass skinTones=true.
GET /api/emoji/parse?text=… segments with Intl.Segmenter, so a zero-width-joiner family such as 👨👩👧👦 counts as one emoji and a flag counts as one, not two. It also returns each distinct emoji with its name and occurrence count.
Yes — /api/emoji/search?q=happy matches the CLDR English annotation keywords as well as the Unicode name, ranking exact name matches first and keyword substring matches last.
Because the device's emoji font predates that release. Every record carries unicodeVersion, the emoji release that introduced the sequence, and the list route's maxVersion filter returns only sequences at or below a release you choose.
/api/emoji/lookup?codepoints=1F468+200D+1F4BB accepts space-, comma-, plus- or hyphen-separated hex, with or without the U+ prefix. U+FE0F is ignored when matching, so an unqualified spelling resolves to the fully-qualified sequence.
Inspect UTF-16 and Unicode scalar values, apply the four standard normalization forms, and segment text into graphemes, words or sentences.
Hostile-but-safe text fixtures: RTL, bidi controls, CJK width, combining marks, ZWJ emoji, zero-width and length bombs — plus pseudo-localization.
UTS #39 skeletons, mixed-script and identifier-status checks: whether two usernames or domains are visual lookalikes, and which characters differ.
Sort and compare strings the way a locale actually orders them: German sorts ä with a, Swedish sorts it after z. Every option visibly changes the answer.
Diagnose and repair mojibake — the byte round-trip that turned café into café and привет into привет — across 28 legacy encodings.