HTTP
Call the API from any language or server with plain HTTP.
On this page
Every feature of the API is plain HTTP with JSON, so any language and any server can use it. The base URL is https://api.emojisense.com, over HTTPS only: a plain http:// request gets 403, so a key is never sent unencrypted. This page shows the basics. The HTTP API reference has every route and field.
Authenticate
| Caller | Key | Sent as |
|---|---|---|
| Servers, bots, CLIs | Secret sk_live_… | Authorization: Bearer sk_live_…. Refused when the request has an Origin header, and never accepted in the URL. |
| Browsers, extensions | Publishable pk_live_… | ?key=pk_live_…. The Origin must be one of the key’s allowed origins. |
| Quick tests | none | Search, reactions and photos work, with a stricter rate limit per IP address. No custom emoji and no analytics. |
Search
curl "https://api.emojisense.com/v1/search?q=late%20night%20coding&limit=5" \
-H "Authorization: Bearer sk_live_…"const params = new URLSearchParams({ q: "late night coding", limit: "5" });
const response = await fetch(`https://api.emojisense.com/v1/search?${params}`, {
headers: { Authorization: `Bearer ${process.env.EMOJISENSE_SECRET_KEY}` },
});
const { results } = await response.json();import os, requests
response = requests.get(
"https://api.emojisense.com/v1/search",
params={"q": "late night coding", "limit": 5},
headers={"Authorization": f"Bearer {os.environ['EMOJISENSE_SECRET_KEY']}"},
timeout=4,
)
results = response.json()["results"]// Browsers and extensions: a publishable key in the URL, so no CORS preflight.
const url = "https://api.emojisense.com/v1/search?q=late%20night%20coding&limit=5&key=pk_live_…";
const response = await fetch(url);The real answer for “late night coding”, captured on 2026-10-02:
{
"query": "late night coding",
"packVersion": "0.1.0",
"model": "bge-m3@1024",
"results": [
{ "emoji": "🧑💻", "id": "1F9D1-200D-1F4BB", "score": 0.94, "source": "alias" },
{ "emoji": "👨💻", "id": "1F468-200D-1F4BB", "score": 0.94, "source": "alias" },
{ "emoji": "👩💻", "id": "1F469-200D-1F4BB", "score": 0.94, "source": "alias" },
{ "emoji": "🌃", "id": "1F303", "score": 0.94, "source": "alias" },
{ "emoji": "🦉", "id": "1F989", "score": 0.88, "source": "alias" }
],
"cached": false,
"degraded": false,
"overLimit": false,
"aliasLocale": "en",
"culture": null
}aliasLocale is the language whose dictionary ranked the query, and culture is null unless you ask for the culture layer (below). The default mode is hybrid: the server merges dictionary and meaning results for you. Use mode=semantic only when you merge with your own on-device results, as the SDKs do. The Server-Timing header shows where the time went, for example embed;dur=450, total;dur=491.
Add culture=1 for the culture layer (off by default): editorial emoji for the moment and the culture join the results after the top answer, with source: "culture", context and cultureId. Add region=GB (an ISO 3166-1 code) for regional entries, for example “football” as ⚽ outside North America. See the API reference.
Other routes
| Route | Guide |
|---|---|
POST /v1/suggest-reactions | Reaction suggestions |
POST /v1/classify-image | Photo to emoji |
GET /v1/pack/:version/… | Pack format: run the engine in your own language |
GET /v1/health | Status, pack version and model |
Errors and limits
400for a missing query or text, a language without a pack or an invalidcultureorregion;401for an unknown or revoked key;403for plainhttp://, an origin that is not allowed, or a secret key in the URL or from a browser;413for an image over 256 KB.429when you call too fast: a key gets 120 requests a minute per IP address, calls without a key 30. Wait for the seconds inRetry-After.- Over the monthly plan limit, the answer is still
200, with"overLimit": true. See Keys and limits.