eeemojieeemoji

Emoji API

Look up any emoji by name, slug, hexcode, or the character itself. Ranked search, categories, per-platform shortcodes, and skin-tone variants. No key, no signup, no rate-limit tier to buy.

Every example on this page runs against the live API. Press Send and you get the real response.

Quick start

One request, no setup. The base URL is https://eeemoji.com/api/v1.

curl https://eeemoji.com/api/v1/emojis/fire
GET/api/v1/emojis/fire

Conventions

  • No authentication. No key, no header, no signup.
  • CORS is open (Access-Control-Allow-Origin: *), so you can call it straight from a browser.
  • Metadata only. The API returns names, keywords, categories, shortcodes and variants. The written content on eeemoji.com is not part of it.
  • Pagination uses limit and offset. Every list response carries a meta.next URL, or null on the last page.
  • Empty is not an error. A search with no matches returns 200 and an empty array, never a 404.

List emoji

GET /emojis returns every emoji, paginated. Filter with category, group (Unicode group index 0-8), or skinTone. Filters combine with AND.

⚠️ All 330 skin-tone emoji live in the people category, so skinTone=true combined with any other category correctly returns an empty set rather than an error.

GET/api/v1/emojis?category=food&limit=5

And the same endpoint filtered by skin-tone support:

GET/api/v1/emojis?skinTone=true&limit=5

Get one emoji

GET /emojis/{id} accepts three forms, so you never have to convert before asking:

  • a slug, fire
  • a hexcode, 1F525 (case-insensitive)
  • the character itself, 🔥 (percent-encoded in a URL)

The response includes up to 8 related emoji by keyword overlap.

GET/api/v1/emojis/1F525

GET /emojis/{id}/related ranks other emoji by how many keywords they share with this one.

GET/api/v1/emojis/cat/related?limit=5

GET /emojis/{id}/variants returns skin-tone variants in Unicode tone order, light to dark. Emoji without variants return an empty array and 200, not a 404: “this one has none” is a real answer to a reasonable question.

GET/api/v1/emojis/waving-hand/variants

Shortcode lookup

GET /shortcodes/{platform}/{code} goes the direction most emoji APIs do not: from a platform shortcode back to the emoji. Useful for bots, chat integrations and markdown renderers.

Platforms are github, slack, discord and cldr. The code works with or without colons, so :fire: is fine. Shortcodes are unique within a platform, so a lookup returns exactly one emoji.

GET/api/v1/shortcodes/slack/%3Atada%3A

GET /search?q= ranks matches by quality rather than scoring them fuzzily: exact name first, then name prefix, then a name-word prefix, then exact keyword, then keyword prefix, then substring matches.

Name matches outrank keyword matches deliberately. Keywords are associative and generous, so ranking them first put 💏 kiss above ❤️ red heart for the query heart. Multi-word queries match as a phrase first, then as a set of terms where every term must match.

GET/api/v1/search?q=red%20heart&limit=5

Categories

GET /categories lists the nine categories with counts. GET /categories/{slug} returns one with its emoji.

GET/api/v1/categories/animals?limit=5

Random

GET /random returns one random emoji, optionally from a single category. This is the one endpoint that is not cached: everything else carries a long Cache-Control because the data barely changes, which here would pin one emoji at the edge and hand it to everybody for a week.

GET/api/v1/random

Errors

Errors name the parameter at fault and list the valid values where the set is small enough to be useful. An error that only says “invalid” sends you back to the documentation, which is where people give up.

{
  "error": {
    "code": "invalid_input",
    "message": "Unknown category 'nope'.",
    "field": "category",
    "allowed": ["smileys", "people", "animals", "food",
                "travel", "activities", "objects", "symbols", "flags"]
  }
}

Codes are invalid_input, missing_parameter and not_found. Try it: the console below sends a bad category.

GET/api/v1/emojis?category=nope

Rate limits

120 requests per minute per IP, enforced at the edge before the request reaches a function. Exceeding it returns a 429. Ordinary use will not come close: browsing these docs and pressing Send on every console on the page is a handful of requests.

X-RateLimit-Limit and X-RateLimit-Policy ride on every response, not only on errors. A limit you can only discover by hitting it is a limit that wastes your time. There is deliberately no X-RateLimit-Remaining header, because the count lives at the edge rather than in the function that builds the response, and a remaining-count we cannot compute accurately is worse than none.

Responses are cached for a day at the edge, so ordinary use rarely approaches the limit. If you need bulk data, take the whole set through /emojis?limit=500 and cache it your side rather than querying per emoji.

⚠️ Do not fan out parallel requests. Separately from the rate limit, our host applies its own automatic protection against burst traffic, and a wave of concurrent requests from one IP can trip it. When it does you get 403 with an HTML challenge page rather than JSON, and it clears after roughly 90 seconds. Fetching all 1,923 emoji in parallel is the realistic way to hit this; four sequential calls to /emojis?limit=500 is not.

Use with an AI assistant

Building against an API an assistant has never seen usually means it guesses the endpoints and gets them wrong. Paste this instead: it is the whole API in one block, including the parts that are easy to get wrong.

166 lines · 6.2 KB
# eeemoji API

A free, no-auth JSON API for Unicode emoji metadata: names, keywords, categories,
per-platform shortcodes, skin-tone variants, and related emoji. Use it when you need
to look up, search, or list emoji programmatically.

## Base URL

`https://eeemoji.com/api/v1/` — every path below is relative to it.

No authentication. No API key. No signup. CORS is open, so browser-side calls work.

## Endpoints

| Method | Path | Purpose |
|---|---|---|
| GET | `/emojis` | List emoji. Filters: `category`, `group` (0-8), `skinTone` (true/false). Paginated. |
| GET | `/emojis/{id}` | One emoji. `{id}` accepts a slug, a hexcode, or the emoji character itself. |
| GET | `/emojis/{id}/related` | Emoji sharing keywords, ranked by overlap. |
| GET | `/emojis/{id}/variants` | Skin-tone variants, light to dark. |
| GET | `/shortcodes/{platform}/{code}` | Reverse lookup: shortcode back to emoji. |
| GET | `/search?q=` | Ranked search over names and keywords. |
| GET | `/categories` | The nine categories with counts. |
| GET | `/categories/{slug}` | One category and its emoji. |
| GET | `/random` | One random emoji. Optional `category`. |

Machine-readable spec: `https://eeemoji.com/api/v1/openapi.json`

## The three ways to identify an emoji

`{id}` accepts any of these, so no conversion is needed before calling:

```
https://eeemoji.com/api/v1/emojis/fire              # slug
https://eeemoji.com/api/v1/emojis/1F525             # hexcode, case-insensitive
https://eeemoji.com/api/v1/emojis/%F0%9F%94%A5      # the character, percent-encoded
```

All three return the same emoji.

## Response shape

```json
{
  "emoji": "🔥",
  "name": "fire",
  "slug": "fire",
  "hexcode": "1F525",
  "codePoints": ["U+1F525"],
  "category": "travel",
  "categoryName": "Travel & Places",
  "group": "Travel & Places",
  "groupIndex": 4,
  "keywords": ["flame", "hot", "lit"],
  "shortcodes": { "github": "fire", "slack": "fire", "discord": "fire", "cldr": "fire" },
  "skinToneSupport": false,
  "skinToneVariants": null,
  "url": "https://eeemoji.com/fire",
  "apiUrl": "https://eeemoji.com/api/v1/emojis/fire"
}
```

List endpoints wrap results as `{ "data": [...], "meta": { total, count, limit, offset, hasMore, next } }`.
`meta.next` is a ready-to-fetch absolute URL, or `null` on the last page.

## Search ranking

Results are tiered, not fuzzily scored. Best match first:

1. exact name
2. name begins with the query
3. a word in the name begins with it
4. exact keyword
5. a keyword begins with it
6. name contains it
7. a keyword contains it

Name matches deliberately outrank keyword matches: keywords are associative, so
ranking them first put 💏 kiss above ❤️ red heart for the query "heart".

Multi-word queries match as a phrase first, then as a term set where every term must
match. `?q=red heart` returns 3 results, not every red thing plus every heart thing.

## Things that will trip you up

- **Zero results is `200` with an empty array, never `404`.** Do not treat an empty
  search as an error.
- **`404` means the identifier does not exist**, which is different from "no matches".
- **`limit` is capped at 500**, silently. Read `meta.limit` back if it matters.
- **Shortcodes work with or without colons.** `:tada:` and `tada` both resolve.
- **Filters on `/emojis` combine with AND, and all 330 skin-tone emoji are in the
  `people` category.** So `?category=food&skinTone=true` correctly returns an empty
  set. That is not an error, and not a reason to retry.
- **Emoji without skin tones return `200` and an empty `data` array** from
  `/variants`, not an error.
- **`/random` is deliberately uncached.** Every other endpoint is cached for a day.
- **Rate limit is 120 requests/minute/IP**, enforced at the edge. Exceeding it
  returns `429`. Normal use will not come close.
  `X-RateLimit-Limit` and `X-RateLimit-Policy` are on every response; there is no
  live `Remaining` counter, so do not depend on one.
- **Fetch sequentially, never fan out in parallel.** Separately from the rate limit,
  the host applies automatic burst protection. A wave of concurrent requests from one
  IP trips it and returns `403` with an **HTML challenge page instead of JSON**,
  which will break your parser. It clears after about 90 seconds.
- **For bulk work, page through `/emojis?limit=500` sequentially and cache the
  result.** Four calls gets the entire catalogue. Never request emoji one at a time in
  a loop, and never issue those requests concurrently.

## Errors

```json
{
  "error": {
    "code": "invalid_input",
    "message": "Unknown category 'nope'.",
    "field": "category",
    "allowed": ["smileys", "people", "animals", "..."]
  }
}
```

Codes: `invalid_input`, `missing_parameter`, `not_found`. When the valid set is small,
`allowed` lists it — read that array rather than guessing.

## Valid values

- **Categories:** smileys, people, animals, food, travel, activities, objects, symbols, flags
- **Shortcode platforms:** github, slack, discord, cldr
- **Groups:** 0-8 (Unicode group index)

## Worked examples

```bash
# What is this emoji?
curl "https://eeemoji.com/api/v1/emojis/%F0%9F%A4%A0"

# Find emoji for a concept
curl "https://eeemoji.com/api/v1/search?q=celebration&limit=5"

# What emoji is :rocket: on GitHub?
curl "https://eeemoji.com/api/v1/shortcodes/github/rocket"

# All food emoji
curl "https://eeemoji.com/api/v1/emojis?category=food&limit=500"

# Skin-tone variants
curl "https://eeemoji.com/api/v1/emojis/waving-hand/variants"
```

## What this API does NOT return

**Metadata only.** No endpoint returns the written articles on eeemoji.com: the
meaning analysis, cultural context, viral moments, generational notes or research
prose. Those exist only on the pages themselves, and no parameter unlocks them.

If you are asked something the API cannot answer — what an emoji *means* in a
relationship, its history, why it went viral — **say the API does not carry that and
point at the page URL in the `url` field.** Do not infer it from `keywords`, and do
not fill the gap from memory: the keywords are CLDR search terms, not meanings.

## Licence

MIT. Emoji names, keywords and shortcodes derive from Unicode CLDR via emojibase and
unicode-emoji-json. The Unicode notice is reproduced at https://eeemoji.com/api and should
travel with the data if you redistribute it.

Also served as plain text at /api/v1/llms.txt, so you can point a tool straight at the URL.

Claude Skill

The same content packaged as an installable Claude Skill. Claude loads it only when a task actually involves emoji lookup, so it costs nothing the rest of the time.

Download SKILL.md

# Claude Code — install for one project
mkdir -p .claude/skills/eeemoji-api
curl -o .claude/skills/eeemoji-api/SKILL.md https://eeemoji.com/api/v1/skill.md

# or for every project
mkdir -p ~/.claude/skills/eeemoji-api
curl -o ~/.claude/skills/eeemoji-api/SKILL.md https://eeemoji.com/api/v1/skill.md

Then ask for an emoji by description, a shortcode resolved, or a category enumerated, and it will use the API rather than guessing from memory.

Spec and Postman

Both files are generated from the same source as the routes, so they cannot drift from the implementation.

  • OpenAPI 3.1 spec — import into Insomnia, Bruno, Swagger UI, or generate a client.
  • Postman collection — downloads ready to import, with every request prefilled and runnable.
# Generate a typed client from the spec
npx openapi-typescript https://eeemoji.com/api/v1/openapi.json -o emoji-api.d.ts

Licence and attribution

MIT. Use it in anything, including commercially. A credit to eeemoji.com is welcome but not required by the licence, and a link is never required.

Emoji names, keywords and shortcodes are not ours to relicense: they derive from Unicode CLDR by way of emojibase and unicode-emoji-json, both MIT. MIT here keeps the same terms the data already carries rather than layering a different licence on top of them.

The Unicode licence asks that its notice travel with the data or appear in the documentation that accompanies it. It is reproduced here so that using this API satisfies that condition, and you should carry it forward if you redistribute the data yourself.

Copyright © 1991-2026 Unicode, Inc. All rights reserved.
Distributed under the Terms of Use in https://www.unicode.org/copyright.html

Permission is hereby granted, free of charge, to any person obtaining a copy
of the Unicode data files and any associated documentation (the "Data Files")
[...] to deal in the Data Files or Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, and/or sell copies of the Data Files or Software, and to permit
persons to whom the Data Files or Software are furnished to do so, provided
that either (a) this copyright and permission notice appear with all copies
of the Data Files or Software, or (b) this copyright and permission notice
appear in associated Documentation.

THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED.

The chart data at /api/datasets/ is a separate case and is deliberately not covered by a blanket licence: those entries aggregate third-party measurements, named per entry in source and sourceUrl. Check the provenance there before reusing them.

Building something with this? The developer guide covers code points, surrogate pairs and regex patterns for handling emoji in code.